From 58cf2aa2dc08b4a9c2633dc5a93863f15d5c0795 Mon Sep 17 00:00:00 2001 From: Alessandro Autiero Date: Sat, 24 Aug 2024 16:27:16 +0200 Subject: [PATCH] Release 0.0.6 --- README.md | 25 +- pom.xml | 35 +- .../it/auties/whatsapp/api/ClientType.java | 6 +- .../whatsapp/api/ConnectionBuilder.java | 1 - .../it/auties/whatsapp/api/ErrorHandler.java | 29 +- .../api/MobileRegistrationBuilder.java | 19 +- .../whatsapp/api/PairingCodeHandler.java | 1 - .../it/auties/whatsapp/api/QrHandler.java | 5 +- .../whatsapp/api/TextPreviewSetting.java | 5 +- .../auties/whatsapp/api/WebHistoryLength.java | 10 +- .../java/it/auties/whatsapp/api/Whatsapp.java | 1369 ++++++++++------- .../whatsapp/controller/Controller.java | 8 +- .../controller/ControllerSerializer.java | 26 +- .../it/auties/whatsapp/controller/Keys.java | 69 +- .../it/auties/whatsapp/controller/Store.java | 61 +- .../DiscardingControllerSerializer.java | 8 +- .../FileControllerSerializer.java} | 159 +- .../builtin/JsonControllerSerializer.java | 85 + .../builtin/ProtobufControllerSerializer.java | 84 + .../it/auties/whatsapp/crypto/AesGcm.java | 31 +- .../auties/whatsapp/crypto/GroupCipher.java | 6 +- .../java/it/auties/whatsapp/crypto/Hkdf.java | 2 +- .../whatsapp/crypto/SessionBuilder.java | 10 +- .../auties/whatsapp/crypto/SessionCipher.java | 12 +- .../whatsapp/exception/RequestException.java | 8 +- .../AppStateHandler.java | 24 +- .../AuthHandler.java | 74 +- .../MessageHandler.java | 94 +- .../SocketHandler.java | 350 +++-- .../SocketHandshake.java | 26 +- .../SocketListener.java | 4 +- .../SocketRequest.java | 19 +- .../implementation/SocketSession.java | 264 ++++ .../SocketState.java | 7 +- .../StreamHandler.java | 198 ++- .../{binary => io}/BinaryDecoder.java | 19 +- .../{binary => io}/BinaryEncoder.java | 4 +- .../whatsapp/{binary => io}/BinaryTag.java | 2 +- .../whatsapp/{binary => io}/BinaryTokens.java | 2 +- .../it/auties/whatsapp/listener/Listener.java | 32 +- .../whatsapp/listener/ListenerConsumer.java | 19 + .../it/auties/whatsapp/listener/OnAction.java | 15 - .../it/auties/whatsapp/listener/OnCall.java | 13 - .../whatsapp/listener/OnChatMessagesSync.java | 14 - .../it/auties/whatsapp/listener/OnChats.java | 20 - .../whatsapp/listener/OnContactBlocked.java | 13 - .../listener/OnContactPictureChanged.java | 13 - .../whatsapp/listener/OnContactPresence.java | 17 - .../auties/whatsapp/listener/OnContacts.java | 16 - .../whatsapp/listener/OnDisconnected.java | 14 - .../auties/whatsapp/listener/OnFeatures.java | 13 - .../listener/OnGroupPictureChange.java | 13 - .../listener/OnHistorySyncProgress.java | 12 - .../whatsapp/listener/OnLinkedDevices.java | 15 - .../auties/whatsapp/listener/OnLoggedIn.java | 15 - .../whatsapp/listener/OnMessageDeleted.java | 15 - .../whatsapp/listener/OnMessageReply.java | 15 - .../whatsapp/listener/OnMessageStatus.java | 13 - .../auties/whatsapp/listener/OnMetadata.java | 16 - .../whatsapp/listener/OnNewContact.java | 14 - .../whatsapp/listener/OnNewMessage.java | 13 - .../auties/whatsapp/listener/OnNewStatus.java | 13 - .../whatsapp/listener/OnNewsletters.java | 15 - .../whatsapp/listener/OnNodeReceived.java | 13 - .../auties/whatsapp/listener/OnNodeSent.java | 13 - .../listener/OnPrivacySettingChanged.java | 14 - .../listener/OnProfilePictureChanged.java | 14 - .../whatsapp/listener/OnRegistrationCode.java | 12 - .../auties/whatsapp/listener/OnSetting.java | 13 - .../whatsapp/listener/OnSocketEvent.java | 13 - .../it/auties/whatsapp/listener/OnStatus.java | 14 - .../whatsapp/listener/OnUserAboutChanged.java | 11 - .../listener/OnUserLocaleChanged.java | 14 - .../whatsapp/listener/OnUserNameChanged.java | 12 - .../listener/OnWhatsappAboutChanged.java | 14 - .../whatsapp/listener/OnWhatsappAction.java | 17 - .../whatsapp/listener/OnWhatsappCall.java | 15 - .../listener/OnWhatsappChatMessagesSync.java | 16 - .../whatsapp/listener/OnWhatsappChats.java | 21 - .../listener/OnWhatsappContactBlocked.java | 15 - .../OnWhatsappContactPictureChanged.java | 15 - .../listener/OnWhatsappContactPresence.java | 19 - .../whatsapp/listener/OnWhatsappContacts.java | 18 - .../listener/OnWhatsappDisconnected.java | 16 - .../whatsapp/listener/OnWhatsappFeatures.java | 16 - .../OnWhatsappGroupPictureChange.java | 15 - .../OnWhatsappHistorySyncProgress.java | 16 - .../listener/OnWhatsappLinkedDevices.java | 17 - .../listener/OnWhatsappLocaleChanged.java | 16 - .../whatsapp/listener/OnWhatsappLoggedIn.java | 19 - .../listener/OnWhatsappMediaStatus.java | 16 - .../listener/OnWhatsappMessageDeleted.java | 17 - .../listener/OnWhatsappMessageReply.java | 17 - .../listener/OnWhatsappMessageStatus.java | 15 - .../whatsapp/listener/OnWhatsappMetadata.java | 19 - .../listener/OnWhatsappNameChanged.java | 15 - .../listener/OnWhatsappNewMessage.java | 15 - .../listener/OnWhatsappNewStatus.java | 15 - .../listener/OnWhatsappNewsletters.java | 17 - .../listener/OnWhatsappNodeReceived.java | 15 - .../whatsapp/listener/OnWhatsappNodeSent.java | 15 - .../OnWhatsappPrivacySettingChanged.java | 16 - .../OnWhatsappProfilePictureChanged.java | 17 - .../listener/OnWhatsappRegistrationCode.java | 15 - .../whatsapp/listener/OnWhatsappSetting.java | 15 - .../listener/OnWhatsappSocketEvent.java | 15 - .../RegisterListenerProcessor.java | 4 +- .../auties/whatsapp/model/action/Action.java | 3 +- .../whatsapp/model/action/AgentAction.java | 4 +- .../action/AndroidUnsupportedActions.java | 4 +- .../model/action/ArchiveChatAction.java | 4 +- .../model/action/ChatAssignmentAction.java | 4 +- .../ChatAssignmentOpenedStatusAction.java | 4 +- .../model/action/ClearChatAction.java | 4 +- .../whatsapp/model/action/ContactAction.java | 4 +- .../model/action/DeleteChatAction.java | 4 +- .../action/DeleteMessageForMeAction.java | 4 +- .../model/action/LabelAssociationAction.java | 4 +- .../model/action/LabelEditAction.java | 4 +- .../model/action/MarkChatAsReadAction.java | 4 +- .../whatsapp/model/action/MuteAction.java | 4 +- .../whatsapp/model/action/NuxAction.java | 4 +- .../whatsapp/model/action/PinAction.java | 4 +- .../model/action/PrimaryVersionAction.java | 4 +- .../model/action/QuickReplyAction.java | 4 +- .../action/RecentEmojiWeightsAction.java | 4 +- .../action/RemoveRecentStickerAction.java | 4 +- .../whatsapp/model/action/StarAction.java | 4 +- .../whatsapp/model/action/StickerAction.java | 4 +- .../model/action/SubscriptionAction.java | 4 +- .../model/action/TimeFormatAction.java | 4 +- .../model/action/UserStatusMuteAction.java | 4 +- .../business/BusinessAccountPayload.java | 7 +- .../model/business/BusinessCategory.java | 5 +- .../model/business/BusinessLocalizedName.java | 5 +- .../model/business/BusinessPrivacyStatus.java | 5 +- .../BusinessVerifiedNameCertificate.java | 7 +- .../business/BusinessVerifiedNameDetails.java | 7 +- .../whatsapp/model/button/base/Button.java | 7 +- .../model/button/base/ButtonActionLink.java | 7 +- .../model/button/base/ButtonBody.java | 8 +- .../{misc => base}/ButtonOpaqueData.java | 9 +- .../button/{misc => base}/ButtonRow.java | 9 +- .../{misc => base}/ButtonRowOpaqueData.java | 9 +- .../button/{misc => base}/ButtonSection.java | 9 +- .../model/button/base/ButtonText.java | 2 + .../SingleSelectReplyButton.java | 9 +- .../button/interactive/InteractiveBody.java | 7 +- .../button/interactive/InteractiveButton.java | 11 +- .../interactive/InteractiveCollection.java | 4 +- .../button/interactive/InteractiveFooter.java | 7 +- .../button/interactive/InteractiveHeader.java | 7 +- .../InteractiveHeaderAttachment.java | 5 +- .../InteractiveHeaderThumbnail.java | 7 +- ...Location.java => InteractiveLocation.java} | 9 +- .../InteractiveLocationAnnotation.java | 14 +- .../interactive/InteractiveNativeFlow.java | 4 +- .../button/interactive/InteractivePoint.java | 7 +- .../interactive/InteractiveResponseBody.java | 7 +- .../button/interactive/InteractiveShop.java | 10 +- .../button/template/TemplateFormatter.java | 8 +- .../HighlyStructuredCurrency.java | 4 +- .../HighlyStructuredDateTime.java | 4 +- .../HighlyStructuredDateTimeComponent.java | 15 +- .../HighlyStructuredDateTimeUnixEpoch.java | 4 +- .../HighlyStructuredDateTimeValue.java | 8 +- .../HighlyStructuredLocalizableParameter.java | 7 +- ...lyStructuredLocalizableParameterValue.java | 8 +- .../HighlyStructuredMessage.java | 4 +- .../template/hsm/HighlyStructuredButton.java | 8 +- .../hsm/HighlyStructuredButtonTemplate.java | 7 +- .../hsm/HighlyStructuredCallButton.java | 4 +- .../hsm/HighlyStructuredFourRowTemplate.java | 4 +- .../HighlyStructuredFourRowTemplateTitle.java | 8 +- .../hsm/HighlyStructuredQuickReplyButton.java | 4 +- .../hsm/HighlyStructuredURLButton.java | 4 +- .../template/hydrated/HydratedButton.java | 8 +- .../template/hydrated/HydratedCallButton.java | 4 +- .../hydrated/HydratedFourRowTemplate.java | 4 +- .../HydratedFourRowTemplateTextTitle.java | 7 +- .../HydratedFourRowTemplateTitle.java | 5 +- .../hydrated/HydratedQuickReplyButton.java | 4 +- .../hydrated/HydratedTemplateButton.java | 7 +- .../template/hydrated/HydratedURLButton.java | 4 +- .../it/auties/whatsapp/model/call/Call.java | 5 +- .../whatsapp/model/call/CallStatus.java | 5 +- .../it/auties/whatsapp/model/chat/Chat.java | 65 +- .../whatsapp/model/chat/ChatDisappear.java | 13 +- .../model/chat/ChatEphemeralTimer.java | 12 +- .../auties/whatsapp/model/chat/ChatMute.java | 7 +- .../whatsapp/model/chat/ChatWallpaper.java | 7 +- .../whatsapp/model/chat/GroupParticipant.java | 7 +- .../model/chat/GroupPastParticipant.java | 13 +- .../model/chat/GroupPastParticipants.java | 7 +- .../auties/whatsapp/model/chat/GroupRole.java | 8 +- .../model/companion/CompanionDevice.java | 197 ++- .../model/companion/CompanionHashState.java | 17 +- .../model/companion/CompanionPatch.java | 15 - .../model/companion/CompanionSyncKey.java | 5 +- .../whatsapp/model/contact/Contact.java | 5 +- .../whatsapp/model/contact/ContactCard.java | 23 +- .../whatsapp/model/contact/ContactStatus.java | 8 +- .../whatsapp/model/info/AdReplyInfo.java | 13 +- .../model/info/BusinessAccountLinkInfo.java | 18 +- .../model/info/BusinessIdentityInfo.java | 21 +- .../whatsapp/model/info/ChatMessageInfo.java | 33 +- .../whatsapp/model/info/ContextInfo.java | 8 +- .../model/info/DeviceContextInfo.java | 13 +- .../model/info/ExternalAdReplyInfo.java | 15 +- .../whatsapp/model/info/NativeFlowInfo.java | 7 +- .../model/info/NewsletterMessageInfo.java | 13 +- .../model/info/NotificationMessageInfo.java | 7 +- .../whatsapp/model/info/PaymentInfo.java | 21 +- .../whatsapp/model/info/ProductListInfo.java | 7 +- .../model/info/WebNotificationsInfo.java | 7 +- .../it/auties/whatsapp/model/jid/Jid.java | 31 +- .../whatsapp/model/media/MediaData.java | 7 +- .../whatsapp/model/media/MediaKeys.java | 4 +- .../whatsapp/model/media/MediaVisibility.java | 8 +- .../media/MutableAttachmentProvider.java | 3 +- .../model/message/button/ButtonsMessage.java | 22 +- .../message/button/ButtonsMessageHeader.java | 5 +- .../button/ButtonsMessageHeaderText.java | 7 +- .../button/ButtonsResponseMessage.java | 10 +- .../message/button/InteractiveMessage.java | 7 +- .../button/InteractiveMessageContent.java | 8 +- .../button/InteractiveResponseMessage.java | 4 +- .../model/message/button/ListMessage.java | 12 +- .../message/button/ListResponseMessage.java | 20 +- .../button/NativeFlowResponseMessage.java | 4 +- .../model/message/button/TemplateMessage.java | 4 +- .../message/button/TemplateReplyMessage.java | 4 +- .../model/message/model/ChatMessageKey.java | 10 +- .../message/model/ContextualMessage.java | 1 + .../message/model/FutureMessageContainer.java | 7 +- .../model/message/model/KeepInChat.java | 7 +- .../model/message/model/KeepInChatType.java | 8 +- .../whatsapp/model/message/model/Message.java | 3 +- .../model/message/model/MessageContainer.java | 112 +- .../model/message/model/MessageReceipt.java | 7 +- .../model/message/model/MessageStatus.java | 5 +- .../PublicServiceAnnouncementStatus.java | 7 +- .../payment/CancelPaymentRequestMessage.java | 4 +- .../payment/DeclinePaymentRequestMessage.java | 4 +- .../message/payment/PaymentInviteMessage.java | 10 +- .../payment/PaymentInvoiceMessage.java | 16 +- .../message/payment/PaymentOrderMessage.java | 14 +- .../payment/RequestPaymentMessage.java | 4 +- .../message/payment/SendPaymentMessage.java | 4 +- .../message/server/DeviceSentMessage.java | 4 +- .../message/server/DeviceSyncMessage.java | 4 +- .../model/message/server/ProtocolMessage.java | 10 +- .../server/SenderKeyDistributionMessage.java | 4 +- .../message/server/StickerSyncRMRMessage.java | 4 +- .../model/message/standard/AudioMessage.java | 17 +- .../model/message/standard/CallMessage.java | 4 +- .../message/standard/ContactMessage.java | 12 +- .../message/standard/ContactsMessage.java | 4 +- .../message/standard/DocumentMessage.java | 75 +- .../standard/EncryptedReactionMessage.java | 4 +- .../message/standard/GroupInviteMessage.java | 12 +- .../model/message/standard/ImageMessage.java | 29 +- .../message/standard/KeepInChatMessage.java | 4 +- .../message/standard/LiveLocationMessage.java | 4 +- .../message/standard/LocationMessage.java | 4 +- .../NewsletterAdminInviteMessage.java | 2 + .../message/standard/PollCreationMessage.java | 14 +- .../message/standard/PollUpdateMessage.java | 25 +- .../message/standard/ProductMessage.java | 4 +- .../message/standard/ReactionMessage.java | 4 +- .../standard/RequestPhoneNumberMessage.java | 4 +- .../message/standard/StickerMessage.java | 20 +- .../model/message/standard/TextMessage.java | 18 +- .../message/standard/VideoOrGifMessage.java | 33 +- .../whatsapp/model/mobile/AccountInfo.java | 6 + .../whatsapp/model/mobile/CountryLocale.java | 5 +- .../whatsapp/model/mobile/PhoneNumber.java | 7 +- .../whatsapp/model/mobile/SixPartsKeys.java | 5 +- .../whatsapp/model/newsletter/Newsletter.java | 15 +- .../newsletter/NewsletterDescription.java | 5 +- .../model/newsletter/NewsletterMetadata.java | 5 +- .../model/newsletter/NewsletterName.java | 5 +- .../model/newsletter/NewsletterPicture.java | 5 +- .../model/newsletter/NewsletterReaction.java | 5 +- .../NewsletterReactionSettings.java | 10 +- .../model/newsletter/NewsletterSettings.java | 5 +- .../model/newsletter/NewsletterState.java | 5 +- .../newsletter/NewsletterViewerMetadata.java | 5 +- .../newsletter/NewsletterViewerRole.java | 6 +- .../whatsapp/model/node/Attributes.java | 8 +- .../model/payment/PaymentBackground.java | 12 +- .../model/payment/PaymentMediaData.java | 7 +- .../whatsapp/model/payment/PaymentMoney.java | 7 +- .../model/poll/PollAdditionalMetadata.java | 7 +- .../whatsapp/model/poll/PollOption.java | 7 +- .../whatsapp/model/poll/PollUpdate.java | 7 +- .../poll/PollUpdateEncryptedMetadata.java | 7 +- .../poll/PollUpdateEncryptedOptions.java | 7 +- .../model/poll/PollUpdateMessageMetadata.java | 7 +- .../model/poll/SelectedPollOption.java | 5 +- .../model/privacy/PrivacySettingEntry.java | 5 +- .../model/privacy/PrivacySettingType.java | 5 +- .../model/privacy/PrivacySettingValue.java | 5 +- .../whatsapp/model/product/Product.java | 7 +- .../model/product/ProductCatalog.java | 7 +- .../model/product/ProductListHeaderImage.java | 7 +- .../model/product/ProductSection.java | 7 +- .../model/product/ProductSectionEntry.java | 7 +- .../model/request/QueryNewsletterRequest.java | 4 +- .../AcceptAdminInviteNewsletterResponse.java | 3 +- .../model/response/ContactAboutResponse.java | 3 +- .../CreateAdminInviteNewsletterResponse.java | 3 +- .../response/NewsletterMuteResponse.java | 3 +- .../response/NewsletterStateResponse.java | 3 +- .../model/response/RegistrationResponse.java | 35 +- .../RevokeAdminInviteNewsletterResponse.java | 3 +- .../response/UserChosenNameResponse.java | 3 +- .../model/setting/AutoDownloadSettings.java | 4 +- .../model/setting/AvatarUserSettings.java | 4 +- .../model/setting/EphemeralSettings.java | 4 +- .../model/setting/GlobalSettings.java | 7 +- .../model/setting/LocaleSettings.java | 4 +- .../model/setting/PushNameSettings.java | 4 +- .../setting/SecurityNotificationSettings.java | 4 +- .../whatsapp/model/setting/Setting.java | 4 +- .../model/setting/UnarchiveChatsSettings.java | 4 +- .../model/signal/auth/ClientFinish.java | 7 +- .../model/signal/auth/ClientHello.java | 7 +- .../model/signal/auth/ClientPayload.java | 25 +- .../signal/auth/CompanionProperties.java | 13 +- .../auth/CompanionRegistrationData.java | 7 +- .../whatsapp/model/signal/auth/DNSSource.java | 14 +- .../model/signal/auth/DeviceIdentity.java | 7 +- .../model/signal/auth/HandshakeMessage.java | 7 +- .../model/signal/auth/KeyIndexList.java | 7 +- .../model/signal/auth/NoiseCertificate.java | 7 +- .../model/signal/auth/ServerHello.java | 7 +- .../signal/auth/SignedDeviceIdentity.java | 7 +- .../signal/auth/SignedDeviceIdentityHMAC.java | 7 +- .../model/signal/auth/SignedKeyIndexList.java | 7 +- .../whatsapp/model/signal/auth/UserAgent.java | 50 +- .../whatsapp/model/signal/auth/Version.java | 7 +- .../model/signal/auth/WebFeatures.java | 13 +- .../whatsapp/model/signal/auth/WebInfo.java | 14 +- .../model/signal/auth/WebPayload.java | 7 +- .../model/signal/keypair/ISignalKeyPair.java | 2 +- .../model/signal/keypair/SignalKeyPair.java | 5 +- .../signal/keypair/SignalPreKeyPair.java | 5 +- .../signal/keypair/SignalSignedKeyPair.java | 5 +- .../signal/message/SenderKeyMessage.java | 6 +- .../message/SignalDistributionMessage.java | 4 +- .../model/signal/message/SignalMessage.java | 6 +- .../signal/message/SignalPreKeyMessage.java | 4 +- .../signal/message/SignalProtocolMessage.java | 9 +- .../model/signal/sender/SenderChainKey.java | 5 +- .../model/signal/sender/SenderKeyName.java | 7 +- .../model/signal/sender/SenderKeyRecord.java | 5 +- .../model/signal/sender/SenderKeyState.java | 7 +- .../model/signal/sender/SenderMessageKey.java | 13 +- .../model/signal/sender/SenderPreKeys.java | 5 +- .../model/signal/session/Session.java | 5 +- .../model/signal/session/SessionAddress.java | 7 +- .../model/signal/session/SessionChain.java | 7 +- .../model/signal/session/SessionPreKey.java | 5 +- .../model/signal/session/SessionState.java | 8 +- .../whatsapp/model/sync/ActionDataSync.java | 7 +- .../model/sync/ActionMessageRangeSync.java | 9 +- .../whatsapp/model/sync/ActionValueSync.java | 7 +- .../AppStateFatalExceptionNotification.java | 7 +- .../whatsapp/model/sync/AppStateSyncKey.java | 7 +- .../model/sync/AppStateSyncKeyData.java | 7 +- .../sync/AppStateSyncKeyFingerprint.java | 7 +- .../model/sync/AppStateSyncKeyId.java | 7 +- .../model/sync/AppStateSyncKeyRequest.java | 7 +- .../model/sync/AppStateSyncKeyShare.java | 7 +- .../model/sync/DeviceListMetadata.java | 7 +- .../auties/whatsapp/model/sync/ExitCode.java | 7 +- .../model/sync/ExternalBlobReference.java | 4 +- .../whatsapp/model/sync/HistorySync.java | 14 +- .../model/sync/HistorySyncConfig.java | 7 +- .../model/sync/HistorySyncMessage.java | 7 +- .../model/sync/HistorySyncNotification.java | 11 +- .../auties/whatsapp/model/sync/IndexSync.java | 7 +- ...nitialSecurityNotificationSettingSync.java | 7 +- .../whatsapp/model/sync/KeyExpiration.java | 7 +- .../it/auties/whatsapp/model/sync/KeyId.java | 7 +- .../model/sync/MediaRetryNotification.java | 13 +- .../whatsapp/model/sync/MutationKeys.java | 12 +- .../whatsapp/model/sync/MutationSync.java | 7 +- .../whatsapp/model/sync/MutationsSync.java | 7 +- .../auties/whatsapp/model/sync/PatchSync.java | 7 +- .../auties/whatsapp/model/sync/PatchType.java | 5 +- .../whatsapp/model/sync/PhotoChange.java | 7 +- .../whatsapp/model/sync/PrimaryFeature.java | 7 +- .../auties/whatsapp/model/sync/PushName.java | 7 +- .../model/sync/RecentEmojiWeight.java | 7 +- .../whatsapp/model/sync/RecordSync.java | 12 +- .../model/sync/ServerErrorReceipt.java | 7 +- .../whatsapp/model/sync/SnapshotSync.java | 7 +- .../whatsapp/model/sync/StickerMetadata.java | 7 +- .../model/sync/SyncActionMessage.java | 7 +- .../auties/whatsapp/model/sync/ValueSync.java | 7 +- .../whatsapp/model/sync/VersionSync.java | 7 +- .../it/auties/whatsapp/net/HttpClient.java | 676 ++++++++ .../java/it/auties/whatsapp/net/Response.java | 34 + .../it/auties/whatsapp/net/SocketClient.java | 1216 +++++++++++++++ .../auties/whatsapp/net/WebSocketClient.java | 744 +++++++++ .../registration/WhatsappMetadata.java | 99 +- .../registration/WhatsappRegistration.java | 129 +- .../auties/whatsapp/socket/SocketSession.java | 278 ---- .../java/it/auties/whatsapp/util/Bytes.java | 18 +- .../util/ConcurrentLinkedHashedDequeue.java | 354 ----- .../whatsapp/util/ConcurrentLinkedSet.java | 432 ++++++ .../it/auties/whatsapp/util/Exceptions.java | 4 +- .../java/it/auties/whatsapp/util/Json.java | 17 +- .../java/it/auties/whatsapp/util/Medias.java | 89 +- .../whatsapp/util/ProtobufFutureMixin.java | 18 - .../whatsapp/util/ProtobufUriMixin.java | 19 - .../whatsapp/util/ProtobufUuidMixin.java | 19 - .../java/it/auties/whatsapp/util/Proxies.java | 49 + .../whatsapp/util/ProxyAuthenticator.java | 81 - .../auties/whatsapp/util/SignalConstants.java | 16 + .../auties/whatsapp/util/Specification.java | 70 - src/main/java/module-info.java | 13 +- ...ax.annotation.processing.AbstractProcessor | 2 +- .../java/it/auties/whatsapp/TestLibrary.java | 39 +- .../java/it/auties/whatsapp/UpdateTokens.java | 6 +- .../whatsapp/example/MobileLoginExample.java | 3 - .../whatsapp/example/WebLoginExample.java | 3 +- .../routine/GenerateListenersLambdas.java | 46 + .../auties/whatsapp/routine/UpdateTokens.java | 137 ++ .../it/auties/whatsapp/util/ConfigUtils.java | 30 + .../auties/whatsapp/util/GithubActions.java | 11 + .../it/auties/whatsapp/util/MediaUtils.java | 15 + src/test/resources/BinaryTokens.java | 2 +- 435 files changed, 6892 insertions(+), 4677 deletions(-) rename src/main/java/it/auties/whatsapp/controller/{ => builtin}/DiscardingControllerSerializer.java (90%) rename src/main/java/it/auties/whatsapp/controller/{ProtobufControllerSerializer.java => builtin/FileControllerSerializer.java} (84%) create mode 100644 src/main/java/it/auties/whatsapp/controller/builtin/JsonControllerSerializer.java create mode 100644 src/main/java/it/auties/whatsapp/controller/builtin/ProtobufControllerSerializer.java rename src/main/java/it/auties/whatsapp/{socket => implementation}/AppStateHandler.java (98%) rename src/main/java/it/auties/whatsapp/{socket => implementation}/AuthHandler.java (77%) rename src/main/java/it/auties/whatsapp/{socket => implementation}/MessageHandler.java (95%) rename src/main/java/it/auties/whatsapp/{socket => implementation}/SocketHandler.java (82%) rename src/main/java/it/auties/whatsapp/{socket => implementation}/SocketHandshake.java (60%) rename src/main/java/it/auties/whatsapp/{socket => implementation}/SocketListener.java (60%) rename src/main/java/it/auties/whatsapp/{socket => implementation}/SocketRequest.java (78%) create mode 100644 src/main/java/it/auties/whatsapp/implementation/SocketSession.java rename src/main/java/it/auties/whatsapp/{socket => implementation}/SocketState.java (77%) rename src/main/java/it/auties/whatsapp/{socket => implementation}/StreamHandler.java (90%) rename src/main/java/it/auties/whatsapp/{binary => io}/BinaryDecoder.java (93%) rename src/main/java/it/auties/whatsapp/{binary => io}/BinaryEncoder.java (99%) rename src/main/java/it/auties/whatsapp/{binary => io}/BinaryTag.java (95%) rename src/main/java/it/auties/whatsapp/{binary => io}/BinaryTokens.java (99%) create mode 100644 src/main/java/it/auties/whatsapp/listener/ListenerConsumer.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnAction.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnCall.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnChatMessagesSync.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnChats.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnContactBlocked.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnContactPictureChanged.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnContactPresence.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnContacts.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnDisconnected.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnFeatures.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnGroupPictureChange.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnHistorySyncProgress.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnLinkedDevices.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnLoggedIn.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnMessageDeleted.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnMessageReply.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnMessageStatus.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnMetadata.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnNewContact.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnNewMessage.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnNewStatus.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnNewsletters.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnNodeReceived.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnNodeSent.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnPrivacySettingChanged.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnProfilePictureChanged.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnRegistrationCode.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnSetting.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnSocketEvent.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnStatus.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnUserAboutChanged.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnUserLocaleChanged.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnUserNameChanged.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappAboutChanged.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappAction.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappCall.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappChatMessagesSync.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappChats.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappContactBlocked.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappContactPictureChanged.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappContactPresence.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappContacts.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappDisconnected.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappFeatures.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappGroupPictureChange.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappHistorySyncProgress.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappLinkedDevices.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappLocaleChanged.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappLoggedIn.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappMediaStatus.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappMessageDeleted.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappMessageReply.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappMessageStatus.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappMetadata.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappNameChanged.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappNewMessage.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappNewStatus.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappNewsletters.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappNodeReceived.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappNodeSent.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappPrivacySettingChanged.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappProfilePictureChanged.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappRegistrationCode.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappSetting.java delete mode 100644 src/main/java/it/auties/whatsapp/listener/OnWhatsappSocketEvent.java rename src/main/java/it/auties/whatsapp/listener/{processor => }/RegisterListenerProcessor.java (97%) rename src/main/java/it/auties/whatsapp/model/button/{misc => base}/ButtonOpaqueData.java (92%) rename src/main/java/it/auties/whatsapp/model/button/{misc => base}/ButtonRow.java (74%) rename src/main/java/it/auties/whatsapp/model/button/{misc => base}/ButtonRowOpaqueData.java (66%) rename src/main/java/it/auties/whatsapp/model/button/{misc => base}/ButtonSection.java (63%) rename src/main/java/it/auties/whatsapp/model/button/{misc => base}/SingleSelectReplyButton.java (53%) rename src/main/java/it/auties/whatsapp/model/button/interactive/{InterativeLocation.java => InteractiveLocation.java} (69%) delete mode 100644 src/main/java/it/auties/whatsapp/model/companion/CompanionPatch.java create mode 100644 src/main/java/it/auties/whatsapp/model/mobile/AccountInfo.java create mode 100644 src/main/java/it/auties/whatsapp/net/HttpClient.java create mode 100644 src/main/java/it/auties/whatsapp/net/Response.java create mode 100644 src/main/java/it/auties/whatsapp/net/SocketClient.java create mode 100644 src/main/java/it/auties/whatsapp/net/WebSocketClient.java delete mode 100644 src/main/java/it/auties/whatsapp/socket/SocketSession.java delete mode 100644 src/main/java/it/auties/whatsapp/util/ConcurrentLinkedHashedDequeue.java create mode 100644 src/main/java/it/auties/whatsapp/util/ConcurrentLinkedSet.java delete mode 100644 src/main/java/it/auties/whatsapp/util/ProtobufFutureMixin.java delete mode 100644 src/main/java/it/auties/whatsapp/util/ProtobufUriMixin.java delete mode 100644 src/main/java/it/auties/whatsapp/util/ProtobufUuidMixin.java create mode 100644 src/main/java/it/auties/whatsapp/util/Proxies.java delete mode 100644 src/main/java/it/auties/whatsapp/util/ProxyAuthenticator.java create mode 100644 src/main/java/it/auties/whatsapp/util/SignalConstants.java delete mode 100644 src/main/java/it/auties/whatsapp/util/Specification.java create mode 100644 src/test/java/it/auties/whatsapp/routine/GenerateListenersLambdas.java create mode 100644 src/test/java/it/auties/whatsapp/routine/UpdateTokens.java create mode 100644 src/test/java/it/auties/whatsapp/util/ConfigUtils.java create mode 100644 src/test/java/it/auties/whatsapp/util/GithubActions.java create mode 100644 src/test/java/it/auties/whatsapp/util/MediaUtils.java diff --git a/README.md b/README.md index 3ef06bef2..4ed381881 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ In short, if you use this library without a malicious intent, you will never get com.github.auties00 cobalt - 0.0.5 + 0.0.6 ``` @@ -67,7 +67,7 @@ In short, if you use this library without a malicious intent, you will never get com.github.auties00 cobalt - 0.0.5 + 0.0.6 @@ -79,23 +79,23 @@ In short, if you use this library without a malicious intent, you will never get - Groovy DSL - Dependency ```groovy - implementation 'com.github.auties00:cobalt:0.0.5' + implementation 'com.github.auties00:cobalt:0.0.6' ``` - Annotation processor (required for @RegisterListener) ```groovy - annotationProcessor 'com.github.auties00:cobalt:0.0.5' + annotationProcessor 'com.github.auties00:cobalt:0.0.6' ``` - Kotlin DSL - Dependency ```groovy - implementation("com.github.auties00:cobalt:0.0.5") + implementation("com.github.auties00:cobalt:0.0.6") ``` - Annotation processor (required for @RegisterListener) ```groovy - annotationProcessor("com.github.auties00:cobalt:0.0.5") + annotationProcessor("com.github.auties00:cobalt:0.0.6") ``` ### Javadocs & Documentation @@ -285,6 +285,10 @@ Now you can connect to your session: ``` to connect to Whatsapp. Remember to handle the result using, for example, `join` to await the connection's result. +Finally, if you want to pause the current thread until the connection is closed, use: + ```java + .awaitDisconnection() + ```
@@ -298,7 +302,8 @@ Remember to handle the result using, for example, `join` to await the connection .addDisconnectedListener(reason -> System.out.printf("Disconnected: %s%n", reason)) // Print a message when disconnected .addNewChatMessageListener(message -> System.out.printf("New message: %s%n", message.toJson())) // Print a message when a new chat message arrives .connect() // Connect to Whatsapp asynchronously - .join(); // Await the result + .join() // Await the result + .awaitDisconnection(); // Wait ```
@@ -316,7 +321,8 @@ Remember to handle the result using, for example, `join` to await the connection .addDisconnectedListener(reason -> System.out.printf("Disconnected: %s%n", reason)) // Print a message when disconnected .addNewChatMessageListener(message -> System.out.printf("New message: %s%n", message.toJson())) // Print a message when a new chat message arrives .connect() // Connect to Whatsapp asynchronously - .join(); // Await the result + .join() // Await the result + .awaitDisconnection(); // Wait ``` @@ -343,7 +349,8 @@ Remember to handle the result using, for example, `join` to await the connection .addDisconnectedListener(reason -> System.out.printf("Disconnected: %s%n", reason)) // Print a message when disconnected .addNewChatMessageListener(message -> System.out.printf("New message: %s%n", message.toJson())) // Print a message when a new chat message arrives .connect() // Connect to Whatsapp asynchronously - .join(); // Await the result + .join() // Await the result + .awaitDisconnection(); // Wait ``` diff --git a/pom.xml b/pom.xml index 41baf86c4..74053d686 100644 --- a/pom.xml +++ b/pom.xml @@ -1,9 +1,9 @@ - + 4.0.0 com.github.auties00 cobalt - 0.0.5 + 0.0.6 ${project.groupId}:${project.artifactId} Standalone fully-featured Whatsapp Web API for Java and Kotlin https://github.com/Auties00/Cobalt @@ -178,7 +178,7 @@ 3.5.0 1.6.13 3.5.1 - 3.0.4 + 3.2.1 5.10.0-M1 2.15.2 1.2 @@ -191,6 +191,9 @@ 1.27 22.11 2023.09.10 + 5.3 + 2.0.13 + 2.6.10 @@ -216,7 +219,7 @@
- it.auties.whatsapp.listener.processor.RegisterListenerProcessor + it.auties.whatsapp.listener.RegisterListenerProcessor @@ -281,6 +284,11 @@ protobuf-base ${protoc.version} + + com.github.auties00 + protobuf-serialization-plugin + ${protoc.version} + @@ -318,6 +326,13 @@ ${plist.version} + + + net.dongliu + apk-parser + ${apk.parser.version} + + com.github.auties00 @@ -341,6 +356,18 @@ ${vcard.version} + + + org.slf4j + slf4j-nop + ${sl4j.version} + + + org.slf4j + slf4j-api + ${sl4j.version} + + org.junit.jupiter diff --git a/src/main/java/it/auties/whatsapp/api/ClientType.java b/src/main/java/it/auties/whatsapp/api/ClientType.java index 91a09cfb7..bef554202 100644 --- a/src/main/java/it/auties/whatsapp/api/ClientType.java +++ b/src/main/java/it/auties/whatsapp/api/ClientType.java @@ -1,13 +1,14 @@ package it.auties.whatsapp.api; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; +import it.auties.protobuf.annotation.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 implements ProtobufEnum { +@ProtobufEnum +public enum ClientType { /** * A standalone client that requires the QR code to be scanned by its companion on log-in Reversed * from Whatsapp Web Client @@ -20,6 +21,7 @@ public enum ClientType implements ProtobufEnum { MOBILE(1); final int index; + ClientType(@ProtobufEnumIndex int index) { this.index = index; } diff --git a/src/main/java/it/auties/whatsapp/api/ConnectionBuilder.java b/src/main/java/it/auties/whatsapp/api/ConnectionBuilder.java index 4e5c6dd66..b294f5535 100644 --- a/src/main/java/it/auties/whatsapp/api/ConnectionBuilder.java +++ b/src/main/java/it/auties/whatsapp/api/ConnectionBuilder.java @@ -6,7 +6,6 @@ import it.auties.whatsapp.controller.StoreKeysPair; import it.auties.whatsapp.model.mobile.PhoneNumber; import it.auties.whatsapp.model.mobile.SixPartsKeys; -import it.auties.whatsapp.util.Bytes; import java.util.List; import java.util.Objects; diff --git a/src/main/java/it/auties/whatsapp/api/ErrorHandler.java b/src/main/java/it/auties/whatsapp/api/ErrorHandler.java index 6b8dccf7f..100a06bfe 100644 --- a/src/main/java/it/auties/whatsapp/api/ErrorHandler.java +++ b/src/main/java/it/auties/whatsapp/api/ErrorHandler.java @@ -1,11 +1,10 @@ package it.auties.whatsapp.api; import it.auties.whatsapp.exception.HmacValidationException; +import it.auties.whatsapp.model.jid.Jid; import it.auties.whatsapp.util.Exceptions; import java.nio.file.Path; -import java.util.concurrent.CompletionException; -import java.util.concurrent.TimeoutException; import java.util.function.BiConsumer; import static it.auties.whatsapp.api.ErrorHandler.Location.*; @@ -64,37 +63,41 @@ static ErrorHandler toFile(Path directory) { * @param printer a consumer that handles the printing of the throwable, can be null * @return a non-null error handler */ - static ErrorHandler defaultErrorHandler(BiConsumer printer) { + private static ErrorHandler defaultErrorHandler(BiConsumer printer) { return (whatsapp, location, throwable) -> { var logger = System.getLogger("ErrorHandler"); + var jid = whatsapp.store() + .jid() + .map(Jid::user) + .orElse("UNKNOWN"); if(location == RECONNECT) { - logger.log(WARNING, "Cannot reconnect: retrying on next timeout"); + logger.log(WARNING, "[{0}] Cannot reconnect: retrying on next timeout", jid); return Result.DISCARD; } - if(throwable instanceof CompletionException && throwable.getCause() instanceof TimeoutException) { - logger.log(WARNING, "Detected possible network anomaly: reconnecting"); - return Result.RECONNECT; - } - - logger.log(ERROR, "Socket failure at %s".formatted(location)); + logger.log(ERROR, "[{0}] Socket failure at {1}", jid, location); if (printer != null) { printer.accept(whatsapp, throwable); } + if(location == LOGIN) { + logger.log(WARNING, "[{0}] Cannot login", jid); + return Result.DISCONNECT; + } + if (location == CRYPTOGRAPHY && whatsapp.store().clientType() == ClientType.MOBILE) { - logger.log(WARNING, "Reconnecting"); + logger.log(WARNING, "[{0}] Reconnecting", jid); return Result.RECONNECT; } if (location == INITIAL_APP_STATE_SYNC || location == CRYPTOGRAPHY || (location == MESSAGE && throwable instanceof HmacValidationException)) { - logger.log(WARNING, "Restore"); + logger.log(WARNING, "[{0}] Restore", jid); return Result.RESTORE; } - logger.log(WARNING, "Ignored failure"); + logger.log(WARNING, "[{0}] Ignored failure", jid); return Result.DISCARD; }; } diff --git a/src/main/java/it/auties/whatsapp/api/MobileRegistrationBuilder.java b/src/main/java/it/auties/whatsapp/api/MobileRegistrationBuilder.java index 80317af39..aa965ea06 100644 --- a/src/main/java/it/auties/whatsapp/api/MobileRegistrationBuilder.java +++ b/src/main/java/it/auties/whatsapp/api/MobileRegistrationBuilder.java @@ -34,6 +34,7 @@ public sealed class MobileRegistrationBuilder { public final static class Unregistered extends MobileRegistrationBuilder { private UnverifiedResult unregisteredResult; private VerificationCodeMethod verificationCodeMethod; + private boolean autocloseCloudVerificationClient; Unregistered(Store store, Keys keys, ErrorHandler errorHandler) { super(store, keys, errorHandler); @@ -88,14 +89,15 @@ public CompletableFuture register(long phoneNumber) { verificationCodeSupplier, verificationCodeMethod ); - return registration.registerPhoneNumber().thenApply(response -> { - var api = Whatsapp.customBuilder() - .store(store) - .keys(keys) - .errorHandler(errorHandler) - .build(); - return this.result = new RegisteredResult(api, Optional.ofNullable(response)); - }); + return registration.registerPhoneNumber() + .thenApplyAsync(response -> { + var api = Whatsapp.customBuilder() + .store(store) + .keys(keys) + .errorHandler(errorHandler) + .build(); + return this.result = new RegisteredResult(api, Optional.ofNullable(response)); + }); } var api = Whatsapp.customBuilder() @@ -106,7 +108,6 @@ public CompletableFuture register(long phoneNumber) { return CompletableFuture.completedFuture(result); } - /** * Asks Whatsapp for a one-time-password to start the registration process * diff --git a/src/main/java/it/auties/whatsapp/api/PairingCodeHandler.java b/src/main/java/it/auties/whatsapp/api/PairingCodeHandler.java index 1057e545b..e76c70ea8 100644 --- a/src/main/java/it/auties/whatsapp/api/PairingCodeHandler.java +++ b/src/main/java/it/auties/whatsapp/api/PairingCodeHandler.java @@ -5,7 +5,6 @@ /** * This interface allows to consume a pairing code sent by WhatsappWeb */ -@FunctionalInterface @SuppressWarnings("unused") public non-sealed interface PairingCodeHandler extends Consumer, WebVerificationHandler { /** diff --git a/src/main/java/it/auties/whatsapp/api/QrHandler.java b/src/main/java/it/auties/whatsapp/api/QrHandler.java index d9ea50562..710f153b4 100644 --- a/src/main/java/it/auties/whatsapp/api/QrHandler.java +++ b/src/main/java/it/auties/whatsapp/api/QrHandler.java @@ -22,7 +22,6 @@ /** * This interface allows to consume a qr code and provides default common implementations to do so */ -@FunctionalInterface @SuppressWarnings("unused") public non-sealed interface QrHandler extends Consumer, WebVerificationHandler { /** @@ -134,8 +133,8 @@ static ToFileConsumer toDesktop() { return; } Desktop.getDesktop().open(path.toFile()); - } catch (IOException exception) { - throw new UncheckedIOException("Cannot open file with desktop", exception); + } catch (Throwable throwable) { + throw new RuntimeException("Cannot open file with desktop", throwable); } }; } diff --git a/src/main/java/it/auties/whatsapp/api/TextPreviewSetting.java b/src/main/java/it/auties/whatsapp/api/TextPreviewSetting.java index b815aff23..d68d01db5 100644 --- a/src/main/java/it/auties/whatsapp/api/TextPreviewSetting.java +++ b/src/main/java/it/auties/whatsapp/api/TextPreviewSetting.java @@ -1,13 +1,14 @@ package it.auties.whatsapp.api; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; +import it.auties.protobuf.annotation.ProtobufEnum; /** * The constants of this enumerated type describe the various types of text preview that can be * used */ -public enum TextPreviewSetting implements ProtobufEnum { +@ProtobufEnum +public enum TextPreviewSetting { /** * 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 diff --git a/src/main/java/it/auties/whatsapp/api/WebHistoryLength.java b/src/main/java/it/auties/whatsapp/api/WebHistoryLength.java index b18c90e99..cfa978db1 100644 --- a/src/main/java/it/auties/whatsapp/api/WebHistoryLength.java +++ b/src/main/java/it/auties/whatsapp/api/WebHistoryLength.java @@ -1,20 +1,20 @@ package it.auties.whatsapp.api; +import it.auties.protobuf.annotation.ProtobufMessage; 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 */ +@ProtobufMessage 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 STANDARD = new WebHistoryLength(59206); private static final WebHistoryLength EXTENDED = new WebHistoryLength(Integer.MAX_VALUE); /** @@ -65,6 +65,6 @@ public boolean isZero() { * @return a boolean */ public boolean isExtended() { - return size > Specification.Whatsapp.DEFAULT_HISTORY_SIZE; + return size > STANDARD.size(); } } diff --git a/src/main/java/it/auties/whatsapp/api/Whatsapp.java b/src/main/java/it/auties/whatsapp/api/Whatsapp.java index b8c05869f..505d94fdf 100644 --- a/src/main/java/it/auties/whatsapp/api/Whatsapp.java +++ b/src/main/java/it/auties/whatsapp/api/Whatsapp.java @@ -14,8 +14,11 @@ import it.auties.whatsapp.crypto.Hkdf; import it.auties.whatsapp.crypto.Hmac; import it.auties.whatsapp.crypto.SessionCipher; -import it.auties.whatsapp.listener.*; -import it.auties.whatsapp.listener.processor.RegisterListenerProcessor; +import it.auties.whatsapp.implementation.SocketHandler; +import it.auties.whatsapp.implementation.SocketState; +import it.auties.whatsapp.listener.Listener; +import it.auties.whatsapp.listener.ListenerConsumer; +import it.auties.whatsapp.listener.RegisterListenerProcessor; import it.auties.whatsapp.model.action.*; import it.auties.whatsapp.model.business.*; import it.auties.whatsapp.model.call.Call; @@ -38,6 +41,8 @@ import it.auties.whatsapp.model.message.standard.NewsletterAdminInviteMessageBuilder; import it.auties.whatsapp.model.message.standard.ReactionMessageBuilder; import it.auties.whatsapp.model.message.standard.TextMessage; +import it.auties.whatsapp.model.mobile.AccountInfo; +import it.auties.whatsapp.model.mobile.CountryLocale; import it.auties.whatsapp.model.newsletter.*; import it.auties.whatsapp.model.node.Attributes; import it.auties.whatsapp.model.node.Node; @@ -51,18 +56,18 @@ import it.auties.whatsapp.model.response.*; import it.auties.whatsapp.model.setting.LocaleSettings; import it.auties.whatsapp.model.setting.PushNameSettings; +import it.auties.whatsapp.model.setting.Setting; import it.auties.whatsapp.model.signal.auth.*; import it.auties.whatsapp.model.signal.keypair.SignalKeyPair; import it.auties.whatsapp.model.sync.*; import it.auties.whatsapp.model.sync.PatchRequest.PatchEntry; import it.auties.whatsapp.model.sync.RecordSync.Operation; -import it.auties.whatsapp.socket.SocketHandler; -import it.auties.whatsapp.socket.SocketState; import it.auties.whatsapp.util.*; import javax.imageio.ImageIO; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.lang.reflect.Method; import java.net.URI; import java.nio.charset.StandardCharsets; import java.time.Instant; @@ -73,6 +78,8 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; +import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.function.Function; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -87,9 +94,24 @@ */ @SuppressWarnings({"unused", "UnusedReturnValue"}) public class Whatsapp { + private static final byte[] ACCOUNT_SIGNATURE_HEADER = {6, 0}; + private static final byte[] DEVICE_MOBILE_SIGNATURE_HEADER = {6, 2}; + private static final int COMPANION_PAIRING_TIMEOUT = 10; + private static final int MAX_COMPANIONS = 5; + // The instances are added and removed when the client connects/disconnects // This is to make sure that the instances remain in memory only as long as it's needed private static final Map instances = new ConcurrentHashMap<>(); + private static final Method registerListenersMethod = getRegisterListenersMethod(); + + private static Method getRegisterListenersMethod() { + try { + var clazz = Class.forName(RegisterListenerProcessor.qualifiedClassName()); + return clazz.getMethod(RegisterListenerProcessor.methodName(), Whatsapp.class); + }catch (ReflectiveOperationException exception) { + return null; + } + } static Optional getInstanceByUuid(UUID uuid) { return Optional.ofNullable(instances.get(uuid)); @@ -99,8 +121,6 @@ static void removeInstanceByUuid(UUID uuid) { instances.remove(uuid); } - private final SocketHandler socketHandler; - /** * Checks if a connection exists * @@ -131,33 +151,29 @@ public static boolean isConnected(String alias) { return SocketHandler.isConnected(alias); } - + private final SocketHandler socketHandler; protected Whatsapp(Store store, Keys keys, ErrorHandler errorHandler, WebVerificationHandler webVerificationHandler) { this.socketHandler = new SocketHandler(this, store, keys, errorHandler, webVerificationHandler); - addDisconnectionHandler(store); + handleDisconnections(store); registerListenersAutomatically(store); } - private static void addDisconnectionHandler(Store store) { - store.addListener((OnDisconnected) (reason) -> { - if (reason != DisconnectReason.RECONNECTING) { + private void handleDisconnections(Store store) { + addDisconnectedListener((reason) -> { + if (reason != DisconnectReason.RECONNECTING && reason != DisconnectReason.RESTORE) { removeInstanceByUuid(store.uuid()); } }); } private void registerListenersAutomatically(Store store) { - if (!store.autodetectListeners()) { + if (!store.autodetectListeners() || registerListenersMethod == null) { return; } try { - var clazz = Class.forName(RegisterListenerProcessor.qualifiedClassName()); - var method = clazz.getMethod(RegisterListenerProcessor.methodName(), Whatsapp.class); - method.invoke(null, this); - }catch (ClassNotFoundException exception) { - // Ignored, this can happen if the compilation environment didn't register the processor - }catch (ReflectiveOperationException exception) { + registerListenersMethod.invoke(null, this); + } catch (ReflectiveOperationException exception) { throw new RuntimeException("Cannot register listeners automatically", exception); } } @@ -352,9 +368,17 @@ public CompletableFuture getGdprAccountInfoStatus() { * @return the same instance wrapped in a completable future */ public CompletableFuture changeName(String newName) { + Validate.isTrue(store().clientType() != ClientType.WEB || !store().device().platform().isBusiness(), + "The business name cannot be changed using the web api"); + if(store().clientType() == ClientType.MOBILE && store().device().platform().isBusiness()) { + var oldName = store().name(); + return socketHandler.updateBusinessCertificate(newName) + .thenRunAsync(() -> socketHandler.onUserChanged(newName, oldName)); + } + var oldName = store().name(); - return socketHandler.sendNode(Node.of("presence", Map.of("name", newName))) - .thenRun(() -> socketHandler.updateUserName(newName, oldName)); + return socketHandler.sendNodeWithNoResponse(Node.of("presence", Map.of("name", newName, "type", "available"))) + .thenRunAsync(() -> socketHandler.onUserChanged(newName, oldName)); } /** @@ -425,6 +449,46 @@ public CompletableFuture sendReaction(MessageInfo message return sendChatMessage(message.parentJid(), MessageContainer.of(reactionMessage)); } + /** + * Forwards a message to another chat + * + * @param chat the non-null chat + * @param messageInfo the message to forward + * @return a future + */ + public CompletableFuture forwardChatMessage(JidProvider chat, ChatMessageInfo messageInfo) { + var message = messageInfo.message() + .contentWithContext() + .map(this::createForwardedMessage) + .or(() -> createForwardedText(messageInfo)) + .orElseThrow(() -> new IllegalArgumentException("This message cannot be forwarded: " + messageInfo.message().type())); + return sendChatMessage(chat, message); + } + + private MessageContainer createForwardedMessage(ContextualMessage messageWithContext) { + var forwardingScore = messageWithContext.contextInfo() + .map(ContextInfo::forwardingScore) + .orElse(0); + var contextInfo = new ContextInfoBuilder() + .forwardingScore(forwardingScore + 1) + .forwarded(true) + .build(); + messageWithContext.setContextInfo(contextInfo); + return MessageContainer.of(messageWithContext); + } + + private Optional createForwardedText(ChatMessageInfo messageInfo) { + return messageInfo.message().textWithNoContextMessage().map(rawText -> { + var contextInfo = new ContextInfoBuilder() + .forwardingScore(1) + .forwarded(true) + .build(); + var textMessage = TextMessage.of(rawText); + textMessage.setContextInfo(contextInfo); + return MessageContainer.of(textMessage); + }); + } + /** * Builds and sends a message from a chat and a message * @@ -537,7 +601,6 @@ public CompletableFuture sendNewsletterMessage(JidProvide return sendNewsletterMessage(chat, MessageContainer.of(message)); } - /** * Builds and sends a message from a chat and a message * @@ -568,18 +631,28 @@ public CompletableFuture sendMessage(JidProvider recipien * @return a CompletableFuture */ public CompletableFuture sendChatMessage(JidProvider recipient, MessageContainer message) { + return sendChatMessage(recipient, message, true); + } + + public CompletableFuture sendChatMessage(JidProvider recipient, MessageContainer message, boolean compose) { Validate.isTrue(!recipient.toJid().hasServer(JidServer.NEWSLETTER), "Use sendNewsletterMessage to send a message in a newsletter"); + var info = buildChatMessage(recipient, message); + var composingFuture = compose && message.type() == MessageType.TEXT ? changePresence(recipient.toJid(), COMPOSING) : CompletableFuture.completedFuture(null); + return composingFuture.thenComposeAsync(deltaResult -> sendMessage(info)); + } + + private ChatMessageInfo buildChatMessage(JidProvider recipient, MessageContainer message) { var timestamp = Clock.nowSeconds(); - var deviceInfo = new DeviceContextInfoBuilder() + var deviceInfo = recipient.toJid().hasServer(JidServer.WHATSAPP) ? new DeviceContextInfoBuilder() .deviceListMetadataVersion(2) - .build(); + .build() : null; var key = new ChatMessageKeyBuilder() .id(ChatMessageKey.randomId()) .chatJid(recipient.toJid()) .fromMe(true) .senderJid(jidOrThrowError()) .build(); - var info = new ChatMessageInfoBuilder() + return new ChatMessageInfoBuilder() .status(MessageStatus.PENDING) .senderJid(jidOrThrowError()) .key(key) @@ -587,7 +660,6 @@ public CompletableFuture sendChatMessage(JidProvider recipient, .timestampSeconds(timestamp) .broadcast(recipient.toJid().hasServer(JidServer.BROADCAST)) .build(); - return sendMessage(info); } /** @@ -653,16 +725,11 @@ public CompletableFuture editMessage(T oldMessage, Me .fromMe(true) .senderJid(jidOrThrowError()) .build(); - var protocol = new ProtocolMessageBuilder() - .protocolType(ProtocolMessage.Type.MESSAGE_EDIT) - .key(key) - .editedMessage(MessageContainer.of(newMessage)) - .build(); var info = new ChatMessageInfoBuilder() .status(MessageStatus.PENDING) .senderJid(jidOrThrowError()) .key(key) - .message(MessageContainer.of(protocol)) + .message(MessageContainer.ofEditedMessage(newMessage)) .timestampSeconds(Clock.nowSeconds()) .broadcast(oldChatInfo.chatJid().hasServer(JidServer.BROADCAST)) .build(); @@ -684,9 +751,6 @@ public CompletableFuture sendStatus(Message message) { public CompletableFuture sendStatus(MessageContainer message) { var timestamp = Clock.nowSeconds(); - var deviceInfo = new DeviceContextInfoBuilder() - .deviceListMetadataVersion(2) - .build(); var key = new ChatMessageKeyBuilder() .id(ChatMessageKey.randomId()) .chatJid(Jid.of("status@broadcast")) @@ -697,7 +761,6 @@ public CompletableFuture sendStatus(MessageContainer message) { .status(MessageStatus.PENDING) .senderJid(jidOrThrowError()) .key(key) - .message(message.withDeviceInfo(deviceInfo)) .timestampSeconds(timestamp) .broadcast(false) .build(); @@ -711,10 +774,8 @@ public CompletableFuture sendStatus(MessageContainer message) { * @return a CompletableFuture */ public CompletableFuture sendMessage(ChatMessageInfo info) { - if(store().clientType() == ClientType.MOBILE) { - throw new IllegalStateException("The mobile API cannot send messages or the account will be banned, if you need to send messages please contact me on Telegram @Auties00"); - } - + var recipient = info.chatJid(); + Validate.isTrue(!recipient.toJid().hasServer(JidServer.NEWSLETTER), "Use sendNewsletterMessage to send a message in a newsletter"); return socketHandler.sendMessage(new MessageSendRequest.Chat(info)) .thenApply(ignored -> info); } @@ -726,10 +787,6 @@ public CompletableFuture sendMessage(ChatMessageInfo info) { * @return a CompletableFuture */ public CompletableFuture sendMessage(NewsletterMessageInfo info) { - if(store().clientType() == ClientType.MOBILE) { - throw new IllegalStateException("The mobile API cannot send messages or the account will be banned, if you need to send messages please contact me on Telegram @Auties00"); - } - return socketHandler.sendMessage(new MessageSendRequest.Newsletter(info)) .thenApply(ignored -> info); } @@ -939,6 +996,23 @@ public CompletableFuture queryGroupMetadata(JidProvider chat) { return socketHandler.queryGroupMetadata(chat.toJid()); } + /** + * Queries this account's info + * + * @return a CompletableFuture + */ + public CompletableFuture queryAccountInfo() { + return socketHandler.sendQuery("get", "urn:xmpp:whatsapp:account", Node.of("account")).thenApplyAsync(result -> { + var accoutNode = result.findNode("account") + .orElseThrow(() -> new NoSuchElementException("Missing account node: " + result)); + var lastRegistration = Clock.parseSeconds(accoutNode.attributes().getLong("last_reg")) + .orElseThrow(() -> new NoSuchElementException("Missing account last_reg: " + accoutNode)); + var creation = Clock.parseSeconds(accoutNode.attributes().getLong("creation")) + .orElseThrow(() -> new NoSuchElementException("Missing account creation: " + accoutNode)); + return new AccountInfo(lastRegistration, creation); + }); + } + /** * Queries a business profile, if available * @@ -984,6 +1058,65 @@ private String parseInviteCode(Node result) { .getRequiredString("code"); } + /** + * Queries the invite link of a group + * + * @param chat the target group + * @return a CompletableFuture + */ + public CompletableFuture queryGroupInviteLink(JidProvider chat) { + return queryGroupInviteCode(chat) + .thenApplyAsync("https://chat.whatsapp.com/%s"::formatted); + } + + /** + * Queries the lists of participants currently waiting to be accepted into the group + * + * @param chat the target group + * @return a CompletableFuture + */ + public CompletableFuture> queryGroupParticipantsPendingApproval(JidProvider chat) { + return socketHandler.sendQuery(chat.toJid(), "get", "w:g2", Node.of("membership_approval_requests")) + .thenApplyAsync(this::parseParticipantsPendingApproval); + } + + private List parseParticipantsPendingApproval(Node node) { + return node.findNode("membership_approval_requests") + .stream() + .map(requests -> requests.findNodes("membership_approval_request")) + .flatMap(Collection::stream) + .map(participant -> participant.attributes().getRequiredJid("user")) + .toList(); + } + + /** + * Changes the approval request status of an array of participants for a group + * + * @param chat the target group + * @param approve whether the participants should be accepted into the group + * @param participants the target participants + * @return a CompletableFuture + */ + public CompletableFuture> changeGroupParticipantPendingApprovalStatus(JidProvider chat, boolean approve, JidProvider... participants) { + var participantsNodes = Arrays.stream(participants) + .map(participantJid -> Node.of("participant", Map.of("jid", participantJid))) + .toList(); + var action = approve ? "approve" : "reject"; + return socketHandler.sendQuery(chat.toJid(), "set", "w:g2", Node.of("membership_requests_action", Node.of(action, participantsNodes))) + .thenApplyAsync(result -> parseParticipantsPendingApprovalChange(result, action)); + } + + private List parseParticipantsPendingApprovalChange(Node node, String action) { + return node.findNode("membership_requests_action") + .flatMap(response -> response.findNode(action)) + .map(requests -> requests.findNodes("participant")) + .stream() + .flatMap(Collection::stream) + .filter(participant -> !participant.attributes().hasKey("error")) + .map(participant -> participant.attributes().getRequiredJid("jid")) + .toList(); + } + /** * Revokes the invite code of a group * @@ -1058,15 +1191,6 @@ private void updateSelfPresence(JidProvider chatJid, ContactStatus presence) { * @return a CompletableFuture */ public CompletableFuture changePresence(JidProvider chatJid, ContactStatus presence) { - var knownPresence = store().findChatByJid(chatJid) - .map(Chat::presences) - .map(entry -> entry.get(jidOrThrowError().toSimpleJid())) - .orElse(null); - if (knownPresence == COMPOSING || knownPresence == RECORDING) { - var node = Node.of("chatstate", Map.of("to", chatJid.toJid()), Node.of("paused")); - return socketHandler.sendNodeWithNoResponse(node); - } - if (presence == COMPOSING || presence == RECORDING) { var tag = presence == RECORDING ? COMPOSING : presence; var node = Node.of("chatstate", @@ -1541,8 +1665,7 @@ public CompletableFuture changeEphemeralTimer(JidProvider chat, ChatEpheme .ephemeralExpiration(timer.period().toSeconds()) .build(); yield sendMessage(chat, message) - .thenRun(() -> { - }); + .thenRun(() -> {}); } case GROUP -> { var body = timer == ChatEphemeralTimer.OFF ? Node.of("not_ephemeral") : Node.of("ephemeral", Map.of("expiration", timer.period() @@ -2267,7 +2390,7 @@ private CompletableFuture linkDevice(byte[] advIdentity, by .build(); var deviceIdentityBytes = DeviceIdentitySpec.encode(deviceIdentity); var accountSignatureMessage = Bytes.concat( - Specification.Whatsapp.ACCOUNT_SIGNATURE_HEADER, + ACCOUNT_SIGNATURE_HEADER, deviceIdentityBytes, advIdentity ); @@ -2292,7 +2415,7 @@ private CompletableFuture linkDevice(byte[] advIdentity, by .validIndexes(knownDevices) .build(); var keyIndexListBytes = KeyIndexListSpec.encode(keyIndexList); - var deviceSignatureMessage = Bytes.concat(Specification.Whatsapp.DEVICE_MOBILE_SIGNATURE_HEADER, keyIndexListBytes); + var deviceSignatureMessage = Bytes.concat(DEVICE_MOBILE_SIGNATURE_HEADER, keyIndexListBytes); var keyAccountSignature = Curve25519.sign(keys().identityKeyPair().privateKey(), deviceSignatureMessage, true); var signedKeyIndexList = new SignedKeyIndexListBuilder() .accountSignature(keyAccountSignature) @@ -2309,13 +2432,13 @@ private CompletableFuture linkDevice(byte[] advIdentity, by private int getMaxLinkedDevices() { var maxDevices = socketHandler.store().properties().get("linked_device_max_count"); if (maxDevices == null) { - return Specification.Whatsapp.MAX_COMPANIONS; + return MAX_COMPANIONS; } try { return Integer.parseInt(maxDevices); } catch (NumberFormatException exception) { - return Specification.Whatsapp.MAX_COMPANIONS; + return MAX_COMPANIONS; } } @@ -2342,15 +2465,13 @@ private CompletableFuture handleCompanionPairing(Node resul private CompletableFuture awaitCompanionRegistration(Jid device) { var future = new CompletableFuture(); - OnLinkedDevices listener = data -> { - if (data.contains(device)) { + addLinkedDevicesListener((Collection data) -> { + if (data.contains(device) && !future.isDone()) { future.complete(null); } - }; - addLinkedDevicesListener(listener); - return future.orTimeout(Specification.Whatsapp.COMPANION_PAIRING_TIMEOUT, TimeUnit.SECONDS) - .exceptionally(ignored -> null) - .thenRun(() -> removeListener(listener)); + }); + return future.orTimeout(COMPANION_PAIRING_TIMEOUT, TimeUnit.SECONDS) + .exceptionally(ignored -> null); } private CompletableFuture handleCompanionEncrypt(Node result, Jid companion, int keyId) { @@ -3091,676 +3212,728 @@ public Whatsapp removeListener(Listener listener) { return this; } - /** - * Registers an action listener - * - * @param onAction the listener to register - * @return the same instance - */ - public Whatsapp addActionListener(OnAction onAction) { - return addListener(onAction); - } + // Generated code from it.auties.whatsapp.routine.GenerateListenersLambda - /** - * Registers a chat recent messages listener - * - * @param onChatRecentMessages the listener to register - * @return the same instance - */ - public Whatsapp addChatMessagesSyncListener(OnChatMessagesSync onChatRecentMessages) { - return addListener(onChatRecentMessages); + public Whatsapp addNodeSentListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onNodeSent(Whatsapp whatsapp, Node outgoing) { + consumer.accept(whatsapp, outgoing); + } + }); + return this; } - /** - * Registers a chats listener - * - * @param onChats the listener to register - * @return the same instance - */ - public Whatsapp addChatsListener(OnChats onChats) { - return addListener(onChats); + public Whatsapp addNodeSentListener(ListenerConsumer.Unary consumer) { + addListener(new Listener() { + @Override + public void onNodeSent(Node outgoing) { + consumer.accept(outgoing); + } + }); + return this; } - /** - * Registers a chats listener - * - * @param onChats the listener to register - * @return the same instance - */ - public Whatsapp addChatsListener(OnWhatsappChats onChats) { - return addListener(onChats); + public Whatsapp addNodeReceivedListener(ListenerConsumer.Unary consumer) { + addListener(new Listener() { + @Override + public void onNodeReceived(Node incoming) { + consumer.accept(incoming); + } + }); + return this; } - /** - * Registers a newsletters listener - * - * @param onNewsletters the listener to register - * @return the same instance - */ - public Whatsapp addNewslettersListener(OnNewsletters onNewsletters) { - return addListener(onNewsletters); + public Whatsapp addNodeReceivedListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onNodeReceived(Whatsapp whatsapp, Node incoming) { + consumer.accept(whatsapp, incoming); + } + }); + return this; } - /** - * Registers a newsletters listener - * - * @param onNewsletters the listener to register - * @return the same instance - */ - public Whatsapp addNewslettersListener(OnWhatsappNewsletters onNewsletters) { - return addListener(onNewsletters); + public Whatsapp addLoggedInListener(ListenerConsumer.Empty consumer) { + addListener(new Listener() { + @Override + public void onLoggedIn() { + consumer.accept(); + } + }); + return this; } - - /** - * Registers a contact presence listener - * - * @param onContactPresence the listener to register - * @return the same instance - */ - public Whatsapp addContactPresenceListener(OnContactPresence onContactPresence) { - return addListener(onContactPresence); + public Whatsapp addLoggedInListener(ListenerConsumer.Unary consumer) { + addListener(new Listener() { + @Override + public void onLoggedIn(Whatsapp whatsapp) { + consumer.accept(whatsapp); + } + }); + return this; } - /** - * Registers a contacts listener - * - * @param onContacts the listener to register - * @return the same instance - */ - public Whatsapp addContactsListener(OnContacts onContacts) { - return addListener(onContacts); + public Whatsapp addMetadataListener(ListenerConsumer.Unary> consumer) { + addListener(new Listener() { + @Override + public void onMetadata(Map metadata) { + consumer.accept(metadata); + } + }); + return this; } - /** - * Registers a message status listener - * - * @param onAnyMessageStatus the listener to register - * @return the same instance - */ - public Whatsapp addMessageStatusListener(OnMessageStatus onAnyMessageStatus) { - return addListener(onAnyMessageStatus); + public Whatsapp addMetadataListener(ListenerConsumer.Binary> consumer) { + addListener(new Listener() { + @Override + public void onMetadata(Whatsapp whatsapp, Map metadata) { + consumer.accept(whatsapp, metadata); + } + }); + return this; } - /** - * Registers a disconnected listener - * - * @param onDisconnected the listener to register - * @return the same instance - */ - public Whatsapp addDisconnectedListener(OnDisconnected onDisconnected) { - return addListener(onDisconnected); + public Whatsapp addDisconnectedListener(ListenerConsumer.Unary consumer) { + addListener(new Listener() { + @Override + public void onDisconnected(DisconnectReason reason) { + consumer.accept(reason); + } + }); + return this; } - /** - * Registers a features listener - * - * @param onFeatures the listener to register - * @return the same instance - */ - public Whatsapp addFeaturesListener(OnFeatures onFeatures) { - return addListener(onFeatures); + public Whatsapp addDisconnectedListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onDisconnected(Whatsapp whatsapp, DisconnectReason reason) { + consumer.accept(whatsapp, reason); + } + }); + return this; } - /** - * Registers a logged in listener - * - * @param onLoggedIn the listener to register - * @return the same instance - */ - public Whatsapp addLoggedInListener(OnLoggedIn onLoggedIn) { - return addListener(onLoggedIn); + public Whatsapp addActionListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onAction(Action action, MessageIndexInfo messageIndexInfo) { + consumer.accept(action, messageIndexInfo); + } + }); + return this; } - /** - * Registers a message deleted listener - * - * @param onMessageDeleted the listener to register - * @return the same instance - */ - public Whatsapp addMessageDeletedListener(OnMessageDeleted onMessageDeleted) { - return addListener(onMessageDeleted); + public Whatsapp addActionListener(ListenerConsumer.Ternary consumer) { + addListener(new Listener() { + @Override + public void onAction(Whatsapp whatsapp, Action action, MessageIndexInfo messageIndexInfo) { + consumer.accept(whatsapp, action, messageIndexInfo); + } + }); + return this; } - /** - * Registers a metadata listener - * - * @param onMetadata the listener to register - * @return the same instance - */ - public Whatsapp addMetadataListener(OnMetadata onMetadata) { - return addListener(onMetadata); + public Whatsapp addSettingListener(ListenerConsumer.Unary consumer) { + addListener(new Listener() { + @Override + public void onSetting(Setting setting) { + consumer.accept(setting); + } + }); + return this; } - /** - * Registers a new contact listener - * - * @param onNewContact the listener to register - * @return the same instance - */ - public Whatsapp addNewContactListener(OnNewContact onNewContact) { - return addListener(onNewContact); + public Whatsapp addSettingListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onSetting(Whatsapp whatsapp, Setting setting) { + consumer.accept(whatsapp, setting); + } + }); + return this; } - /** - * Registers a new message listener - * - * @param onNewMessage the listener to register - * @return the same instance - */ - public Whatsapp addNewChatMessageListener(OnNewMessage onNewMessage) { - return addListener(onNewMessage); + public Whatsapp addFeaturesListener(ListenerConsumer.Unary> consumer) { + addListener(new Listener() { + @Override + public void onFeatures(List features) { + Listener.super.onFeatures(features); + } + }); + return this; } - /** - * Registers a new status listener - * - * @param onNewMediaStatus the listener to register - * @return the same instance - */ - public Whatsapp addNewStatusListener(OnNewStatus onNewMediaStatus) { - return addListener(onNewMediaStatus); + public Whatsapp addFeaturesListener(ListenerConsumer.Binary> consumer) { + addListener(new Listener() { + @Override + public void onFeatures(Whatsapp whatsapp, List features) { + consumer.accept(whatsapp, features); + } + }); + return this; } - /** - * Registers a received node listener - * - * @param onNodeReceived the listener to register - * @return the same instance - */ - public Whatsapp addNodeReceivedListener(OnNodeReceived onNodeReceived) { - return addListener(onNodeReceived); + public Whatsapp addContactsListener(ListenerConsumer.Unary> consumer) { + addListener(new Listener() { + @Override + public void onContacts(Collection contacts) { + consumer.accept(contacts); + } + }); + return this; } - /** - * Registers a sent node listener - * - * @param onNodeSent the listener to register - * @return the same instance - */ - public Whatsapp addNodeSentListener(OnNodeSent onNodeSent) { - return addListener(onNodeSent); + public Whatsapp addContactsListener(ListenerConsumer.Binary> consumer) { + addListener(new Listener() { + @Override + public void onContacts(Whatsapp whatsapp, Collection contacts) { + consumer.accept(whatsapp, contacts); + } + }); + return this; } - /** - * Registers a setting listener - * - * @param onSetting the listener to register - * @return the same instance - */ - public Whatsapp addSettingListener(OnSetting onSetting) { - return addListener(onSetting); + public Whatsapp addContactPresenceListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onContactPresence(Chat chat, JidProvider jid) { + consumer.accept(chat, jid); + } + }); + return this; } - /** - * Registers a status listener - * - * @param onMediaStatus the listener to register - * @return the same instance - */ - public Whatsapp addMediaStatusListener(OnStatus onMediaStatus) { - return addListener(onMediaStatus); + public Whatsapp addContactPresenceListener(ListenerConsumer.Ternary consumer) { + addListener(new Listener() { + @Override + public void onContactPresence(Whatsapp whatsapp, Chat chat, JidProvider jid) { + consumer.accept(whatsapp, chat, jid); + } + }); + return this; } - /** - * Registers an event listener - * - * @param onSocketEvent the listener to register - * @return the same instance - */ - public Whatsapp addSocketEventListener(OnSocketEvent onSocketEvent) { - return addListener(onSocketEvent); + public Whatsapp addChatsListener(ListenerConsumer.Unary> consumer) { + addListener(new Listener() { + @Override + public void onChats(Collection chats) { + consumer.accept(chats); + } + }); + return this; } - /** - * Registers an action listener - * - * @param onAction the listener to register - * @return the same instance - */ - public Whatsapp addActionListener(OnWhatsappAction onAction) { - return addListener(onAction); + public Whatsapp addChatsListener(ListenerConsumer.Binary> consumer) { + addListener(new Listener() { + @Override + public void onChats(Whatsapp whatsapp, Collection chats) { + consumer.accept(whatsapp, chats); + } + }); + return this; } - /** - * Registers a sync progress listener - * - * @param onSyncProgress the listener to register - * @return the same instance - */ - public Whatsapp addHistorySyncProgressListener(OnHistorySyncProgress onSyncProgress) { - return addListener(onSyncProgress); + public Whatsapp addNewslettersListener(ListenerConsumer.Unary> consumer) { + addListener(new Listener() { + @Override + public void onNewsletters(Collection newsletters) { + consumer.accept(newsletters); + } + }); + return this; } - /** - * Registers a chat recent messages listener - * - * @param onChatRecentMessages the listener to register - * @return the same instance - */ - public Whatsapp addChatMessagesSyncListener(OnWhatsappChatMessagesSync onChatRecentMessages) { - return addListener(onChatRecentMessages); + public Whatsapp addNewslettersListener(ListenerConsumer.Binary> consumer) { + addListener(new Listener() { + @Override + public void onNewsletters(Whatsapp whatsapp, Collection newsletters) { + consumer.accept(whatsapp, newsletters); + } + }); + return this; } - /** - * Registers a contact presence listener - * - * @param onContactPresence the listener to register - * @return the same instance - */ - public Whatsapp addContactPresenceListener(OnWhatsappContactPresence onContactPresence) { - return addListener(onContactPresence); + public Whatsapp addChatMessagesSyncListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onChatMessagesSync(Chat chat, boolean last) { + consumer.accept(chat, last); + } + }); + return this; } - /** - * Registers a contacts listener - * - * @param onContacts the listener to register - * @return the same instance - */ - public Whatsapp addContactsListener(OnWhatsappContacts onContacts) { - return addListener(onContacts); + public Whatsapp addChatMessagesSyncListener(ListenerConsumer.Ternary consumer) { + addListener(new Listener() { + @Override + public void onChatMessagesSync(Whatsapp whatsapp, Chat chat, boolean last) { + consumer.accept(whatsapp, chat, last); + } + }); + return this; } - /** - * Registers a message status listener - * - * @param onMessageStatus the listener to register - * @return the same instance - */ - public Whatsapp addMessageStatusListener(OnWhatsappMessageStatus onMessageStatus) { - return addListener(onMessageStatus); + public Whatsapp addHistorySyncProgressListener(ListenerConsumer.Ternary consumer) { + addListener(new Listener() { + @Override + public void onHistorySyncProgress(Whatsapp whatsapp, int percentage, boolean recent) { + consumer.accept(whatsapp, percentage, recent); + } + }); + return this; } - /** - * Registers a disconnected listener - * - * @param onDisconnected the listener to register - * @return the same instance - */ - public Whatsapp addDisconnectedListener(OnWhatsappDisconnected onDisconnected) { - return addListener(onDisconnected); + public Whatsapp addHistorySyncProgressListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onHistorySyncProgress(int percentage, boolean recent) { + consumer.accept(percentage, recent); + } + }); + return this; } - /** - * Registers a features listener - * - * @param onFeatures the listener to register - * @return the same instance - */ - public Whatsapp addFeaturesListener(OnWhatsappFeatures onFeatures) { - return addListener(onFeatures); + public Whatsapp addNewMessageListener(ListenerConsumer.Unary consumer) { + addListener(new Listener() { + @Override + public void onNewMessage(MessageInfo info) { + consumer.accept(info); + } + }); + return this; } - /** - * Registers a logged in listener - * - * @param onLoggedIn the listener to register - * @return the same instance - */ - public Whatsapp addLoggedInListener(OnWhatsappLoggedIn onLoggedIn) { - return addListener(onLoggedIn); + public Whatsapp addNewChatMessageListener(ListenerConsumer.Unary consumer) { + addListener(new Listener() { + @Override + public void onNewMessage(MessageInfo info) { + if(info instanceof ChatMessageInfo chatMessageInfo) { + consumer.accept(chatMessageInfo); + } + } + }); + return this; } - /** - * Registers a message deleted listener - * - * @param onMessageDeleted the listener to register - * @return the same instance - */ - public Whatsapp addMessageDeletedListener(OnWhatsappMessageDeleted onMessageDeleted) { - return addListener(onMessageDeleted); + public Whatsapp addNewNewsletterMessageListener(ListenerConsumer.Unary consumer) { + addListener(new Listener() { + @Override + public void onNewMessage(MessageInfo info) { + if(info instanceof NewsletterMessageInfo newsletterMessageInfo) { + consumer.accept(newsletterMessageInfo); + } + } + }); + return this; } - /** - * Registers a metadata listener - * - * @param onMetadata the listener to register - * @return the same instance - */ - public Whatsapp addMetadataListener(OnWhatsappMetadata onMetadata) { - return addListener(onMetadata); + public Whatsapp addNewMessageListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onNewMessage(Whatsapp whatsapp, MessageInfo info) { + consumer.accept(whatsapp, info); + } + }); + return this; } - /** - * Registers a new message listener - * - * @param onNewMessage the listener to register - * @return the same instance - */ - public Whatsapp addNewChatMessageListener(OnWhatsappNewMessage onNewMessage) { - return addListener(onNewMessage); + public Whatsapp addNewNewsletterMessageListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onNewMessage(Whatsapp whatsapp, MessageInfo info) { + if(info instanceof NewsletterMessageInfo newsletterMessageInfo) { + consumer.accept(whatsapp, newsletterMessageInfo); + } + } + }); + return this; } - /** - * Registers a new status listener - * - * @param onNewStatus the listener to register - * @return the same instance - */ - public Whatsapp addNewStatusListener(OnWhatsappNewStatus onNewStatus) { - return addListener(onNewStatus); + public Whatsapp addNewChatMessageListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onNewMessage(Whatsapp whatsapp, MessageInfo info) { + if(info instanceof ChatMessageInfo chatMessageInfo) { + consumer.accept(whatsapp, chatMessageInfo); + } + } + }); + return this; } - /** - * Registers a received node listener - * - * @param onNodeReceived the listener to register - * @return the same instance - */ - public Whatsapp addNodeReceivedListener(OnWhatsappNodeReceived onNodeReceived) { - return addListener(onNodeReceived); + public Whatsapp addMessageDeletedListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onMessageDeleted(MessageInfo info, boolean everyone) { + consumer.accept(info, everyone); + } + }); + return this; } - /** - * Registers a sent node listener - * - * @param onNodeSent the listener to register - * @return the same instance - */ - public Whatsapp addNodeSentListener(OnWhatsappNodeSent onNodeSent) { - return addListener(onNodeSent); + public Whatsapp addMessageDeletedListener(ListenerConsumer.Ternary consumer) { + addListener(new Listener() { + @Override + public void onMessageDeleted(Whatsapp whatsapp, MessageInfo info, boolean everyone) { + consumer.accept(whatsapp, info, everyone); + } + }); + return this; } - /** - * Registers a setting listener - * - * @param onSetting the listener to register - * @return the same instance - */ - public Whatsapp addSettingListener(OnWhatsappSetting onSetting) { - return addListener(onSetting); + public Whatsapp addMessageStatusListener(ListenerConsumer.Unary consumer) { + addListener(new Listener() { + @Override + public void onMessageStatus(MessageInfo info) { + consumer.accept(info); + } + }); + return this; } - /** - * Registers a status listener - * - * @param onStatus the listener to register - * @return the same instance - */ - public Whatsapp addMediaStatusListener(OnWhatsappMediaStatus onStatus) { - return addListener(onStatus); + public Whatsapp addMessageStatusListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onMessageStatus(Whatsapp whatsapp, MessageInfo info) { + consumer.accept(whatsapp, info); + } + }); + return this; } - /** - * Registers an event listener - * - * @param onSocketEvent the listener to register - * @return the same instance - */ - public Whatsapp addSocketEventListener(OnWhatsappSocketEvent onSocketEvent) { - return addListener(onSocketEvent); + public Whatsapp addStatusListener(ListenerConsumer.Unary> consumer) { + addListener(new Listener() { + @Override + public void onStatus(Collection status) { + consumer.accept(status); + } + }); + return this; } - /** - * Registers a sync progress listener - * - * @param onSyncProgress the listener to register - * @return the same instance - */ - public Whatsapp addHistorySyncProgressListener(OnWhatsappHistorySyncProgress onSyncProgress) { - return addListener(onSyncProgress); + public Whatsapp addStatusListener(ListenerConsumer.Binary> consumer) { + addListener(new Listener() { + @Override + public void onStatus(Whatsapp whatsapp, Collection status) { + consumer.accept(whatsapp, status); + } + }); + return this; } - /** - * Registers a message reply listener - * - * @param onMessageReply the listener to register - * @return the same instance - */ - public Whatsapp addMessageReplyListener(OnWhatsappMessageReply onMessageReply) { - return addListener(onMessageReply); + public Whatsapp addNewStatusListener(ListenerConsumer.Unary consumer) { + addListener(new Listener() { + @Override + public void onNewStatus(ChatMessageInfo status) { + consumer.accept(status); + } + }); + return this; } - /** - * Registers a message reply listener for a specific message - * - * @param info the non-null target message - * @param onMessageReply the non-null listener - */ - public Whatsapp addMessageReplyListener(ChatMessageInfo info, OnMessageReply onMessageReply) { - return addMessageReplyListener(info.id(), onMessageReply); + public Whatsapp addNewStatusListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onNewStatus(Whatsapp whatsapp, ChatMessageInfo status) { + consumer.accept(whatsapp, status); + } + }); + return this; } - /** - * Registers a message reply listener - * - * @param onMessageReply the listener to register - * @return the same instance - */ - public Whatsapp addMessageReplyListener(OnMessageReply onMessageReply) { - return addListener(onMessageReply); + public Whatsapp addSocketEventListener(ListenerConsumer.Unary consumer) { + addListener(new Listener() { + @Override + public void onSocketEvent(SocketEvent event) { + consumer.accept(event); + } + }); + return this; } - /** - * Registers a message reply listener for a specific message - * - * @param info the non-null target message - * @param onMessageReply the non-null listener - */ - public Whatsapp addMessageReplyListener(ChatMessageInfo info, OnWhatsappMessageReply onMessageReply) { - return addMessageReplyListener(info.id(), onMessageReply); + public Whatsapp addSocketEventListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onSocketEvent(Whatsapp whatsapp, SocketEvent event) { + consumer.accept(whatsapp, event); + } + }); + return this; } - /** - * Registers a message reply listener for a specific message - * - * @param id the non-null id of the target message - * @param onMessageReply the non-null listener - */ - public Whatsapp addMessageReplyListener(String id, OnMessageReply onMessageReply) { - return addMessageReplyListener((info, quoted) -> { - if (!info.id().equals(id)) { - return; + public Whatsapp addMessageReplyListener(ListenerConsumer.Ternary consumer) { + addListener(new Listener() { + @Override + public void onMessageReply(Whatsapp whatsapp, ChatMessageInfo response, QuotedMessageInfo quoted) { + consumer.accept(whatsapp, response, quoted); } + }); + return this; + } - onMessageReply.onMessageReply(info, quoted); + public Whatsapp addMessageReplyListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onMessageReply(ChatMessageInfo response, QuotedMessageInfo quoted) { + consumer.accept(response, quoted); + } }); + return this; } - /** - * Registers a message reply listener for a specific message - * - * @param id the non-null id of the target message - * @param onMessageReply the non-null listener - */ - public Whatsapp addMessageReplyListener(String id, OnWhatsappMessageReply onMessageReply) { - return addMessageReplyListener(((whatsapp, info, quoted) -> { - if (!info.id().equals(id)) { - return; + public Whatsapp addProfilePictureChangedListener(ListenerConsumer.Unary consumer) { + addListener(new Listener() { + @Override + public void onProfilePictureChanged(Contact contact) { + consumer.accept(contact); } + }); + return this; + } - onMessageReply.onMessageReply(whatsapp, info, quoted); - })); + public Whatsapp addProfilePictureChangedListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onProfilePictureChanged(Whatsapp whatsapp, Contact contact) { + consumer.accept(whatsapp, contact); + } + }); + return this; } - /** - * Registers a name change listener - * - * @param onUserNameChanged the non-null listener - */ - public Whatsapp addNameChangedListener(OnUserNameChanged onUserNameChanged) { - return addListener(onUserNameChanged); + public Whatsapp addGroupPictureChangedListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onGroupPictureChanged(Whatsapp whatsapp, Chat group) { + consumer.accept(whatsapp, group); + } + }); + return this; } - /** - * Registers a name change listener - * - * @param onNameChange the non-null listener - */ - public Whatsapp addNameChangedListener(OnWhatsappNameChanged onNameChange) { - return addListener(onNameChange); + public Whatsapp addGroupPictureChangedListener(ListenerConsumer.Unary consumer) { + addListener(new Listener() { + @Override + public void onGroupPictureChanged(Chat group) { + consumer.accept(group); + } + }); + return this; } - /** - * Registers a status change listener - * - * @param onUserAboutChanged the non-null listener - */ - public Whatsapp addAboutChangedListener(OnUserAboutChanged onUserAboutChanged) { - return addListener(onUserAboutChanged); + public Whatsapp addNameChangedListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onNameChanged(String oldName, String newName) { + consumer.accept(oldName, newName); + } + }); + return this; } - /** - * Registers a status change listener - * - * @param onUserStatusChange the non-null listener - */ - public Whatsapp addAboutChangedListener(OnWhatsappAboutChanged onUserStatusChange) { - return addListener(onUserStatusChange); + public Whatsapp addNameChangedListener(ListenerConsumer.Ternary consumer) { + addListener(new Listener() { + @Override + public void onNameChanged(Whatsapp whatsapp, String oldName, String newName) { + consumer.accept(whatsapp, oldName, newName); + } + }); + return this; } - /** - * Registers a picture change listener - * - * @param onProfilePictureChanged the non-null listener - */ - public Whatsapp addUserPictureChangedListener(OnProfilePictureChanged onProfilePictureChanged) { - return addListener(onProfilePictureChanged); + public Whatsapp addAboutChangedListener(ListenerConsumer.Ternary consumer) { + addListener(new Listener() { + @Override + public void onAboutChanged(Whatsapp whatsapp, String oldAbout, String newAbout) { + consumer.accept(whatsapp, oldAbout, newAbout); + } + }); + return this; } - /** - * Registers a picture change listener - * - * @param onUserPictureChange the non-null listener - */ - public Whatsapp addUserPictureChangedListener(OnWhatsappProfilePictureChanged onUserPictureChange) { - return addListener(onUserPictureChange); + public Whatsapp addAboutChangedListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onAboutChanged(String oldAbout, String newAbout) { + consumer.accept(oldAbout, newAbout); + } + }); + return this; } - /** - * Registers a profile picture listener - * - * @param onContactPictureChanged the non-null listener - */ - public Whatsapp addContactPictureChangedListener(OnContactPictureChanged onContactPictureChanged) { - return addListener(onContactPictureChanged); + public Whatsapp addLocaleChangedListener(ListenerConsumer.Ternary consumer) { + addListener(new Listener() { + @Override + public void onLocaleChanged(Whatsapp whatsapp, CountryLocale oldLocale, CountryLocale newLocale) { + consumer.accept(whatsapp, oldLocale, newLocale); + } + }); + return this; } - /** - * Registers a profile picture listener - * - * @param onProfilePictureChange the non-null listener - */ - public Whatsapp addContactPictureChangedListener(OnWhatsappContactPictureChanged onProfilePictureChange) { - return addListener(onProfilePictureChange); + public Whatsapp addLocaleChangedListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onLocaleChanged(CountryLocale oldLocale, CountryLocale newLocale) { + consumer.accept(oldLocale, newLocale); + } + }); + return this; } - /** - * Registers a group picture listener - * - * @param onGroupPictureChange the non-null listener - */ - public Whatsapp addGroupPictureChangedListener(OnGroupPictureChange onGroupPictureChange) { - return addListener(onGroupPictureChange); + public Whatsapp addContactBlockedListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onContactBlocked(Whatsapp whatsapp, Contact contact) { + consumer.accept(whatsapp, contact); + } + }); + return this; } - /** - * Registers a group picture listener - * - * @param onGroupPictureChange the non-null listener - */ - public Whatsapp addGroupPictureChangedListener(OnWhatsappGroupPictureChange onGroupPictureChange) { - return addListener(onGroupPictureChange); + public Whatsapp addContactBlockedListener(ListenerConsumer.Unary consumer) { + addListener(new Listener() { + @Override + public void onContactBlocked(Contact contact) { + consumer.accept(contact); + } + }); + return this; } - /** - * Registers a contact blocked listener - * - * @param onContactBlocked the non-null listener - */ - public Whatsapp addContactBlockedListener(OnContactBlocked onContactBlocked) { - return addListener(onContactBlocked); + public Whatsapp addNewContactListener(ListenerConsumer.Unary consumer) { + addListener(new Listener() { + @Override + public void onNewContact(Contact contact) { + consumer.accept(contact); + } + }); + return this; } - /** - * Registers a contact blocked listener - * - * @param onContactBlocked the non-null listener - */ - public Whatsapp addContactBlockedListener(OnWhatsappContactBlocked onContactBlocked) { - return addListener(onContactBlocked); + public Whatsapp addNewContactListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onNewContact(Whatsapp whatsapp, Contact contact) { + consumer.accept(whatsapp, contact); + } + }); + return this; } - /** - * Registers a privacy setting changed listener - * - * @param onPrivacySettingChanged the listener to register - * @return the same instance - */ - public Whatsapp addPrivacySettingChangedListener(OnPrivacySettingChanged onPrivacySettingChanged) { - return addListener(onPrivacySettingChanged); + public Whatsapp addPrivacySettingChangedListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onPrivacySettingChanged(PrivacySettingEntry oldPrivacyEntry, PrivacySettingEntry newPrivacyEntry) { + consumer.accept(oldPrivacyEntry, newPrivacyEntry); + } + }); + return this; } + public Whatsapp addPrivacySettingChangedListener(ListenerConsumer.Ternary consumer) { + addListener(new Listener() { + @Override + public void onPrivacySettingChanged(Whatsapp whatsapp, PrivacySettingEntry oldPrivacyEntry, PrivacySettingEntry newPrivacyEntry) { + consumer.accept(whatsapp, oldPrivacyEntry, newPrivacyEntry); + } + }); + return this; + } - /** - * Registers a privacy setting changed listener - * - * @param onWhatsappPrivacySettingChanged the listener to register - * @return the same instance - */ - public Whatsapp addPrivacySettingChangedListener(OnWhatsappPrivacySettingChanged onWhatsappPrivacySettingChanged) { - return addListener(onWhatsappPrivacySettingChanged); + public Whatsapp addLinkedDevicesListener(ListenerConsumer.Unary> consumer) { + addListener(new Listener() { + @Override + public void onLinkedDevices(Collection devices) { + consumer.accept(devices); + } + }); + return this; } - /** - * Registers a companion devices changed listener - * - * @param onLinkedDevices the listener to register - * @return the same instance - */ - public Whatsapp addLinkedDevicesListener(OnLinkedDevices onLinkedDevices) { - return addListener(onLinkedDevices); + public Whatsapp addLinkedDevicesListener(ListenerConsumer.Binary> consumer) { + addListener(new Listener() { + @Override + public void onLinkedDevices(Whatsapp whatsapp, Collection devices) { + Listener.super.onLinkedDevices(whatsapp, devices); + } + }); + return this; } - /** - * Registers a companion devices changed listener - * - * @param onWhatsappLinkedDevices the listener to register - * @return the same instance - */ - public Whatsapp addLinkedDevicesListener(OnWhatsappLinkedDevices onWhatsappLinkedDevices) { - return addListener(onWhatsappLinkedDevices); + public Whatsapp addRegistrationCodeListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onRegistrationCode(Whatsapp whatsapp, long code) { + consumer.accept(whatsapp, code); + } + }); + return this; } - /** - * Registers a registration code listener for the mobile api - * - * @param onRegistrationCode the listener to register - * @return the same instance - */ - public Whatsapp addRegistrationCodeListener(OnRegistrationCode onRegistrationCode) { - return addListener(onRegistrationCode); + public Whatsapp addRegistrationCodeListener(ListenerConsumer.Unary consumer) { + addListener(new Listener() { + @Override + public void onRegistrationCode(long code) { + consumer.accept(code); + } + }); + return this; } - /** - * Registers a registration code listener for the mobile api - * - * @param onWhatsappRegistrationCode the listener to register - * @return the same instance - */ - public Whatsapp addRegistrationCodeListener(OnWhatsappRegistrationCode onWhatsappRegistrationCode) { - return addListener(onWhatsappRegistrationCode); + public Whatsapp addCallListener(ListenerConsumer.Unary consumer) { + addListener(new Listener() { + @Override + public void onCall(Call call) { + consumer.accept(call); + } + }); + return this; } - /** - * Registers a call listener - * - * @param onCall the listener to register - * @return the same instance - */ - public Whatsapp addCallListener(OnCall onCall) { - return addListener(onCall); + public Whatsapp addCallListener(ListenerConsumer.Binary consumer) { + addListener(new Listener() { + @Override + public void onCall(Whatsapp whatsapp, Call call) { + consumer.accept(whatsapp, call); + } + }); + return this; } - /** - * Registers a call listener - * - * @param onWhatsappCall the listener to register - * @return the same instance - */ - public Whatsapp addCallListener(OnWhatsappCall onWhatsappCall) { - return addListener(onWhatsappCall); + public Whatsapp addMessageReplyListener(ChatMessageInfo info, Consumer onMessageReply) { + return addMessageReplyListener(info.id(), onMessageReply); + } + + public Whatsapp addMessageReplyListener(ChatMessageInfo info, BiConsumer onMessageReply) { + return addMessageReplyListener(info.id(), onMessageReply); + } + + public Whatsapp addMessageReplyListener(String id, Consumer consumer) { + return addListener(new Listener() { + @Override + public void onNewMessage(MessageInfo info) { + if (!info.id().equals(id)) { + return; + } + + consumer.accept(info); + } + }); + } + + public Whatsapp addMessageReplyListener(String id, BiConsumer consumer) { + return addListener(new Listener() { + @Override + public void onNewMessage(Whatsapp whatsapp, MessageInfo info) { + if (!info.id().equals(id)) { + return; + } + + consumer.accept(whatsapp, info); + } + }); } private Jid jidOrThrowError() { diff --git a/src/main/java/it/auties/whatsapp/controller/Controller.java b/src/main/java/it/auties/whatsapp/controller/Controller.java index f1d5e5e4a..9e0b78b85 100644 --- a/src/main/java/it/auties/whatsapp/controller/Controller.java +++ b/src/main/java/it/auties/whatsapp/controller/Controller.java @@ -1,13 +1,12 @@ package it.auties.whatsapp.controller; import com.fasterxml.jackson.annotation.JsonIgnore; +import it.auties.protobuf.annotation.ProtobufMessage; 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.*; @@ -16,11 +15,12 @@ * way to store IDs and serialize said class. */ @SuppressWarnings("unused") -public abstract sealed class Controller> implements ProtobufMessage permits Store, Keys { +@ProtobufMessage +public abstract sealed class Controller> permits Store, Keys { /** * The id of this controller */ - @ProtobufProperty(index = 1, type = ProtobufType.STRING, mixin = ProtobufUuidMixin.class) + @ProtobufProperty(index = 1, type = ProtobufType.STRING) protected final UUID uuid; /** diff --git a/src/main/java/it/auties/whatsapp/controller/ControllerSerializer.java b/src/main/java/it/auties/whatsapp/controller/ControllerSerializer.java index 790ac015a..eff049b88 100644 --- a/src/main/java/it/auties/whatsapp/controller/ControllerSerializer.java +++ b/src/main/java/it/auties/whatsapp/controller/ControllerSerializer.java @@ -1,6 +1,9 @@ package it.auties.whatsapp.controller; import it.auties.whatsapp.api.ClientType; +import it.auties.whatsapp.controller.builtin.DiscardingControllerSerializer; +import it.auties.whatsapp.controller.builtin.JsonControllerSerializer; +import it.auties.whatsapp.controller.builtin.ProtobufControllerSerializer; import it.auties.whatsapp.model.mobile.PhoneNumber; import java.nio.file.Path; @@ -22,7 +25,7 @@ public interface ControllerSerializer { * @return a serializer */ static ControllerSerializer discarding() { - return DiscardingControllerSerializer.singleton(); + return DiscardingControllerSerializer.of(); } /** @@ -46,6 +49,27 @@ static ControllerSerializer toProtobuf(Path baseDirectory) { return ProtobufControllerSerializer.of(baseDirectory); } + /** + * Returns a json serializer + * This implementation uses .json files with no compression + * + * @return a serializer + */ + static ControllerSerializer toJson() { + return JsonControllerSerializer.ofDefaultPath(); + } + + /** + * Returns the default serializer + * This implementation uses .json files with no compression + * + * @param baseDirectory the directory where all the sessions should be saved + * @return a serializer + */ + static ControllerSerializer toJson(Path baseDirectory) { + return JsonControllerSerializer.of(baseDirectory); + } + /** * Returns all the known IDs * diff --git a/src/main/java/it/auties/whatsapp/controller/Keys.java b/src/main/java/it/auties/whatsapp/controller/Keys.java index 54a988b47..4aafeb35e 100644 --- a/src/main/java/it/auties/whatsapp/controller/Keys.java +++ b/src/main/java/it/auties/whatsapp/controller/Keys.java @@ -3,11 +3,10 @@ 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.annotation.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; @@ -25,10 +24,10 @@ import it.auties.whatsapp.model.sync.PatchType; import it.auties.whatsapp.util.Bytes; import it.auties.whatsapp.util.Clock; -import it.auties.whatsapp.util.ProtobufUuidMixin; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; @@ -40,7 +39,8 @@ * This controller holds the cryptographic-related data regarding a WhatsappWeb session */ @SuppressWarnings({"unused", "UnusedReturnValue"}) -public final class Keys extends Controller implements ProtobufMessage { +@ProtobufMessage +public final class Keys extends Controller { /** * The client id */ @@ -75,7 +75,7 @@ public final class Keys extends Controller implements ProtobufMessage { * The signed pre key */ @ProtobufProperty(index = 10, type = ProtobufType.OBJECT) - final SignalSignedKeyPair signedKeyPair; + SignalSignedKeyPair signedKeyPair; /** * The signed key of the companion's device @@ -108,7 +108,7 @@ public final class Keys extends Controller implements ProtobufMessage { @ProtobufProperty(index = 15, type = ProtobufType.BYTES) final byte[] deviceId; - @ProtobufProperty(index = 26, type = ProtobufType.STRING, mixin = ProtobufUuidMixin.class) + @ProtobufProperty(index = 26, type = ProtobufType.STRING) final UUID advertisingId; /** @@ -117,6 +117,12 @@ public final class Keys extends Controller implements ProtobufMessage { @ProtobufProperty(index = 16, type = ProtobufType.BYTES) final byte[] identityId; + /** + * The recovery token for the mobile api + */ + @ProtobufProperty(index = 27, type = ProtobufType.BYTES) + final byte[] backupToken; + /** * The bytes of the encoded {@link SignedDeviceIdentityHMAC} received during the auth process */ @@ -126,7 +132,7 @@ public final class Keys extends Controller implements ProtobufMessage { /** * Sender keys for signal implementation */ - @ProtobufProperty(index = 18, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.OBJECT) + @ProtobufProperty(index = 18, type = ProtobufType.MAP, mapKeyType = ProtobufType.STRING, mapValueType = ProtobufType.OBJECT) final Map senderKeys; /** @@ -138,18 +144,18 @@ public final class Keys extends Controller implements ProtobufMessage { /** * Sessions map */ - @ProtobufProperty(index = 20, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.OBJECT) - final Map sessions; + @ProtobufProperty(index = 20, type = ProtobufType.MAP, mapKeyType = ProtobufType.STRING, mapValueType = ProtobufType.OBJECT) + final ConcurrentMap sessions; /** * Hash state */ - @ProtobufProperty(index = 21, type = ProtobufType.OBJECT) - final List hashStates; + @ProtobufProperty(index = 21, type = ProtobufType.OBJECT, mapKeyType = ProtobufType.STRING, mapValueType = ProtobufType.OBJECT) + final ConcurrentMap hashStates; - @ProtobufProperty(index = 22, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.OBJECT) - final Map groupsPreKeys; + @ProtobufProperty(index = 22, type = ProtobufType.MAP, mapKeyType = ProtobufType.STRING, mapValueType = ProtobufType.OBJECT) + final ConcurrentMap groupsPreKeys; /** * Whether the client was registered @@ -188,7 +194,7 @@ public final class Keys extends Controller implements ProtobufMessage { byte[] writeKey, readKey; @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - public Keys(UUID uuid, PhoneNumber phoneNumber, ClientType clientType, Collection alias, Integer registrationId, SignalKeyPair noiseKeyPair, SignalKeyPair ephemeralKeyPair, SignalKeyPair identityKeyPair, SignalKeyPair companionKeyPair, SignalSignedKeyPair signedKeyPair, byte[] signedKeyIndex, Long signedKeyIndexTimestamp, List preKeys, String fdid, byte[] deviceId, UUID advertisingId, byte[] identityId, SignedDeviceIdentity companionIdentity, Map senderKeys, List appStateKeys, Map sessions, List hashStates, Map groupsPreKeys, boolean registered, boolean businessCertificate, boolean initialAppSync) { + public Keys(UUID uuid, PhoneNumber phoneNumber, ClientType clientType, Collection alias, Integer registrationId, SignalKeyPair noiseKeyPair, SignalKeyPair ephemeralKeyPair, SignalKeyPair identityKeyPair, SignalKeyPair companionKeyPair, SignalSignedKeyPair signedKeyPair, byte[] signedKeyIndex, Long signedKeyIndexTimestamp, List preKeys, String fdid, byte[] deviceId, UUID advertisingId, byte[] identityId, byte[] backupToken, SignedDeviceIdentity companionIdentity, Map senderKeys, List appStateKeys, ConcurrentMap sessions, ConcurrentMap hashStates, ConcurrentMap groupsPreKeys, boolean registered, boolean businessCertificate, boolean initialAppSync) { super(uuid, phoneNumber, null, clientType, alias); this.registrationId = Objects.requireNonNullElseGet(registrationId, () -> ThreadLocalRandom.current().nextInt(16380) + 1); this.noiseKeyPair = Objects.requireNonNull(noiseKeyPair, "Missing noise keypair"); @@ -203,11 +209,12 @@ public Keys(UUID uuid, PhoneNumber phoneNumber, ClientType clientType, Collectio this.deviceId = Objects.requireNonNullElseGet(deviceId, () -> HexFormat.of().parseHex(UUID.randomUUID().toString().replaceAll("-", ""))); this.advertisingId = Objects.requireNonNullElseGet(advertisingId, UUID::randomUUID); this.identityId = Objects.requireNonNull(identityId, "Missing identity id"); + this.backupToken = Objects.requireNonNullElseGet(backupToken, () -> Bytes.random(20)); this.companionIdentity = companionIdentity; this.senderKeys = Objects.requireNonNullElseGet(senderKeys, ConcurrentHashMap::new); this.appStateKeys = Objects.requireNonNullElseGet(appStateKeys, ArrayList::new); this.sessions = Objects.requireNonNullElseGet(sessions, ConcurrentHashMap::new); - this.hashStates = Objects.requireNonNullElseGet(hashStates, ArrayList::new); + this.hashStates = Objects.requireNonNullElseGet(hashStates, ConcurrentHashMap::new); this.groupsPreKeys = Objects.requireNonNullElseGet(groupsPreKeys, ConcurrentHashMap::new); this.registered = registered; this.businessCertificate = businessCertificate; @@ -324,10 +331,7 @@ public Optional findAppKeyById(Jid jid, byte[] id) { * @return a non-null hash state */ public Optional findHashStateByName(Jid device, PatchType patchType) { - return hashStates.stream() - .filter(hashState -> Objects.equals(hashState.companion(), device) && hashState.state().type() == patchType) - .findFirst() - .map(CompanionPatch::state); + return Optional.ofNullable(hashStates.get("%s_%s".formatted(device, patchType))); } /** @@ -371,8 +375,7 @@ public Keys putSession(SessionAddress address, Session record) { * @return this */ public Keys putState(Jid device, CompanionHashState state) { - var hashState = new CompanionPatch(device, state); - hashStates.add(hashState); + hashStates.put("%s_%s".formatted(device, state.type()), state); return this; } @@ -574,24 +577,8 @@ public byte[] identityId() { return this.identityId; } - public Map senderKeys() { - return this.senderKeys; - } - - public List appStateKeys() { - return appStateKeys; - } - - public Map sessions() { - return this.sessions; - } - - public List hashStates() { - return hashStates; - } - - public Map groupsPreKeys() { - return this.groupsPreKeys; + public byte[] backupToken() { + return backupToken; } public boolean registered() { @@ -622,6 +609,10 @@ public Optional readKey() { return Optional.ofNullable(this.readKey); } + public void setSignedKeyPair(SignalSignedKeyPair signedKeyPair) { + this.signedKeyPair = signedKeyPair; + } + public Keys setCompanionKeyPair(SignalKeyPair companionKeyPair) { this.companionKeyPair = companionKeyPair; return this; diff --git a/src/main/java/it/auties/whatsapp/controller/Store.java b/src/main/java/it/auties/whatsapp/controller/Store.java index 84951b7fa..741504fe8 100644 --- a/src/main/java/it/auties/whatsapp/controller/Store.java +++ b/src/main/java/it/auties/whatsapp/controller/Store.java @@ -4,13 +4,14 @@ import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonValue; -import it.auties.protobuf.annotation.ProtobufConverter; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; +import it.auties.protobuf.annotation.ProtobufSerializer; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.api.ClientType; import it.auties.whatsapp.api.TextPreviewSetting; import it.auties.whatsapp.api.WebHistoryLength; +import it.auties.whatsapp.implementation.SocketRequest; import it.auties.whatsapp.listener.Listener; import it.auties.whatsapp.model.business.BusinessCategory; import it.auties.whatsapp.model.call.Call; @@ -40,8 +41,8 @@ import it.auties.whatsapp.model.signal.auth.Version; import it.auties.whatsapp.model.sync.HistorySyncMessage; import it.auties.whatsapp.registration.WhatsappMetadata; -import it.auties.whatsapp.socket.SocketRequest; -import it.auties.whatsapp.util.*; +import it.auties.whatsapp.util.Bytes; +import it.auties.whatsapp.util.Clock; import java.net.URI; import java.time.Duration; @@ -60,17 +61,22 @@ * This controller holds the user-related data regarding a WhatsappWeb session */ @SuppressWarnings({"unused", "UnusedReturnValue"}) -public final class Store extends Controller implements ProtobufMessage { +@ProtobufMessage +public final class Store extends Controller { + /** + * Default push name + */ + private static final String DEFAULT_NAME = "User"; + /** * The version used by this session */ - @ProtobufProperty(index = 5, type = ProtobufType.STRING, mixin = ProtobufUriMixin.class) + @ProtobufProperty(index = 5, type = ProtobufType.STRING) URI proxy; /** * The version used by this session */ - @ProtobufProperty(index = 6, type = ProtobufType.OBJECT, overrideType = Version.class, mixin = ProtobufFutureMixin.class) CompletableFuture version; /** @@ -152,14 +158,14 @@ public final class Store extends Controller implements ProtobufMessage { * The key here is the index of the device's key * The value is the device's companion jid */ - @ProtobufProperty(index = 18, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.INT32) + @ProtobufProperty(index = 18, type = ProtobufType.MAP, mapKeyType = ProtobufType.STRING, mapValueType = 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. */ - @ProtobufProperty(index = 19, type = ProtobufType.STRING, mixin = ProtobufUriMixin.class) + @ProtobufProperty(index = 19, type = ProtobufType.STRING) URI profilePicture; /** @@ -185,7 +191,7 @@ public final class Store extends Controller implements ProtobufMessage { /** * The non-null map of properties received by whatsapp */ - @ProtobufProperty(index = 23, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.STRING) + @ProtobufProperty(index = 23, type = ProtobufType.MAP, mapKeyType = ProtobufType.STRING, mapValueType = ProtobufType.STRING) final ConcurrentHashMap properties; /** @@ -197,7 +203,7 @@ public final class Store extends Controller implements ProtobufMessage { /** * The non-null map of contacts */ - @ProtobufProperty(index = 24, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.OBJECT) + @ProtobufProperty(index = 24, type = ProtobufType.MAP, mapKeyType = ProtobufType.STRING, mapValueType = ProtobufType.OBJECT) final ConcurrentHashMap contacts; /** @@ -215,13 +221,13 @@ public final class Store extends Controller implements ProtobufMessage { /** * The non-null map of privacy settings */ - @ProtobufProperty(index = 26, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.OBJECT) + @ProtobufProperty(index = 26, type = ProtobufType.MAP, mapKeyType = ProtobufType.STRING, mapValueType = ProtobufType.OBJECT) final ConcurrentHashMap privacySettings; /** * The non-null map of calls */ - @ProtobufProperty(index = 27, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.OBJECT) + @ProtobufProperty(index = 27, type = ProtobufType.MAP, mapKeyType = ProtobufType.STRING, mapValueType = ProtobufType.OBJECT) final ConcurrentHashMap calls; /** @@ -337,17 +343,12 @@ public final class Store extends Controller implements ProtobufMessage { * All args constructor */ @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - public Store(UUID uuid, PhoneNumber phoneNumber, ClientType clientType, Collection alias, URI proxy, CompletableFuture version, boolean online, CountryLocale locale, String name, String verifiedName, 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 automaticPresenceUpdates, boolean automaticMessageReceipts, ReleaseChannel releaseChannel, CompanionDevice device, boolean checkPatchMacs) { + public Store(UUID uuid, PhoneNumber phoneNumber, ClientType clientType, Collection alias, URI proxy, boolean online, CountryLocale locale, String name, String verifiedName, 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 automaticPresenceUpdates, boolean automaticMessageReceipts, ReleaseChannel releaseChannel, CompanionDevice device, boolean checkPatchMacs) { super(uuid, phoneNumber, null, clientType, alias); - if (proxy != null) { - ProxyAuthenticator.globalAuthenticator().register(proxy); - } - this.proxy = proxy; - this.version = version; this.online = online; this.locale = locale; - this.name = Objects.requireNonNullElse(name, Specification.Whatsapp.DEFAULT_NAME); + this.name = Objects.requireNonNullElse(name, DEFAULT_NAME); this.verifiedName = verifiedName; this.businessAddress = businessAddress; this.businessLongitude = businessLongitude; @@ -396,7 +397,7 @@ public static Store newStore(UUID uuid, Long phoneNumber, Collection ali .device(clientType == ClientType.MOBILE ? CompanionDevice.ios(false) : CompanionDevice.web()) .clientType(clientType) .alias(alias) - .name(Specification.Whatsapp.DEFAULT_NAME) + .name(DEFAULT_NAME) .jid(phoneNumber != null ? Jid.of(phoneNumber) : null) .autodetectListeners(true) .automaticPresenceUpdates(true) @@ -442,6 +443,16 @@ public Collection contacts() { return Collections.unmodifiableCollection(contacts.values()); } + /** + * Checks if a contact is in memory + * + * @param jidProvider the non-null jid + * @return a boolean + */ + public boolean hasContact(JidProvider jidProvider) { + return jidProvider != null && contacts.get(jidProvider.toJid()) != null; + } + /** * Queries every contact whose name is equal to {@code name} * @@ -1160,12 +1171,6 @@ public Store removeListeners() { * @return the same instance */ public Store setProxy(URI proxy) { - if (proxy != null && proxy.getUserInfo() != null) { - ProxyAuthenticator.globalAuthenticator().register(proxy); - } else if (proxy == null && this.proxy != null) { - ProxyAuthenticator.globalAuthenticator().unregister(this.proxy); - } - this.proxy = proxy; return this; } @@ -1539,7 +1544,7 @@ public AsyncVersion(Version initialValue, Supplier> d } } - @ProtobufConverter + @ProtobufSerializer @JsonValue public Version value() { if (future != null) { diff --git a/src/main/java/it/auties/whatsapp/controller/DiscardingControllerSerializer.java b/src/main/java/it/auties/whatsapp/controller/builtin/DiscardingControllerSerializer.java similarity index 90% rename from src/main/java/it/auties/whatsapp/controller/DiscardingControllerSerializer.java rename to src/main/java/it/auties/whatsapp/controller/builtin/DiscardingControllerSerializer.java index a045b02fd..35662e261 100644 --- a/src/main/java/it/auties/whatsapp/controller/DiscardingControllerSerializer.java +++ b/src/main/java/it/auties/whatsapp/controller/builtin/DiscardingControllerSerializer.java @@ -1,18 +1,20 @@ -package it.auties.whatsapp.controller; +package it.auties.whatsapp.controller.builtin; import it.auties.whatsapp.api.ClientType; +import it.auties.whatsapp.controller.*; import it.auties.whatsapp.model.mobile.PhoneNumber; import java.util.LinkedList; import java.util.Optional; import java.util.UUID; import java.util.concurrent.CompletableFuture; -class DiscardingControllerSerializer implements ControllerSerializer { + +public class DiscardingControllerSerializer implements ControllerSerializer { private static final DiscardingControllerSerializer SINGLETON = new DiscardingControllerSerializer(); private static final LinkedList EMPTY_IDS = new LinkedList<>(); private static final LinkedList EMPTY_PHONE_NUMBERS = new LinkedList<>(); - public static ControllerSerializer singleton() { + public static ControllerSerializer of() { return SINGLETON; } diff --git a/src/main/java/it/auties/whatsapp/controller/ProtobufControllerSerializer.java b/src/main/java/it/auties/whatsapp/controller/builtin/FileControllerSerializer.java similarity index 84% rename from src/main/java/it/auties/whatsapp/controller/ProtobufControllerSerializer.java rename to src/main/java/it/auties/whatsapp/controller/builtin/FileControllerSerializer.java index 1794f41e3..573ae150b 100644 --- a/src/main/java/it/auties/whatsapp/controller/ProtobufControllerSerializer.java +++ b/src/main/java/it/auties/whatsapp/controller/builtin/FileControllerSerializer.java @@ -1,15 +1,17 @@ -package it.auties.whatsapp.controller; +package it.auties.whatsapp.controller.builtin; import it.auties.whatsapp.api.ClientType; +import it.auties.whatsapp.controller.Controller; +import it.auties.whatsapp.controller.ControllerSerializer; +import it.auties.whatsapp.controller.Keys; +import it.auties.whatsapp.controller.Store; import it.auties.whatsapp.model.chat.Chat; import it.auties.whatsapp.model.chat.ChatBuilder; -import it.auties.whatsapp.model.chat.ChatSpec; import it.auties.whatsapp.model.info.ContextInfo; import it.auties.whatsapp.model.jid.Jid; import it.auties.whatsapp.model.message.model.ContextualMessage; import it.auties.whatsapp.model.mobile.PhoneNumber; import it.auties.whatsapp.model.newsletter.Newsletter; -import it.auties.whatsapp.model.newsletter.NewsletterSpec; import it.auties.whatsapp.model.sync.HistorySyncMessage; import java.io.IOException; @@ -30,61 +32,58 @@ import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; -class ProtobufControllerSerializer implements ControllerSerializer { - private static final Path DEFAULT_SERIALIZER_PATH = Path.of(System.getProperty("user.home") + "/.cobalt/"); +abstract class FileControllerSerializer implements ControllerSerializer { private static final String CHAT_PREFIX = "chat_"; private static final String NEWSLETTER_PREFIX = "newsletter_"; - private static final String STORE_NAME = "store.proto"; - private static final String KEYS_NAME = "keys.proto"; - - private static final Map serializers = new ConcurrentHashMap<>(); - static { - serializers.put(DEFAULT_SERIALIZER_PATH, new ProtobufControllerSerializer(DEFAULT_SERIALIZER_PATH)); - } - - - private final Path baseDirectory; - private final ConcurrentMap> attributeStoreSerializers; - private LinkedList cachedUuids; - private LinkedList cachedPhoneNumbers; - - public static ControllerSerializer ofDefaultPath() { - return Objects.requireNonNull(serializers.get(DEFAULT_SERIALIZER_PATH)); - } - - public static ControllerSerializer of(Path baseDirectory) { - var known = serializers.get(baseDirectory); - if(known != null) { - return known; - } - - var result = new ProtobufControllerSerializer(baseDirectory); - serializers.put(baseDirectory, result); - return result; - } - - private ProtobufControllerSerializer(Path baseDirectory) { + + final Path baseDirectory; + final ConcurrentMap> attributeStoreSerializers; + LinkedList cachedUuids; + final Object uuidsLock; + LinkedList cachedPhoneNumbers; + final Object phoneNumbersLock; + FileControllerSerializer(Path baseDirectory) { this.baseDirectory = baseDirectory; this.attributeStoreSerializers = new ConcurrentHashMap<>(); + this.uuidsLock = new Object(); + this.phoneNumbersLock = new Object(); } + abstract String fileExtension(); + + abstract byte[] encodeKeys(Keys keys); + abstract byte[] encodeStore(Store store); + abstract byte[] encodeChat(Chat chat); + abstract byte[] encodeNewsletter(Newsletter newsletter); + + abstract Keys decodeKeys(byte[] keys); + abstract Store decodeStore(byte[] store); + abstract Chat decodeChat(byte[] chat); + abstract Newsletter decodeNewsletter(byte[] newsletter); + @Override public LinkedList listIds(ClientType type) { if (cachedUuids != null) { return new ImmutableLinkedList<>(cachedUuids); } + + synchronized (uuidsLock) { + if (cachedUuids != null) { + return new ImmutableLinkedList<>(cachedUuids); + } + + var directory = getHome(type); + if (Files.notExists(directory)) { + return ImmutableLinkedList.empty(); + } - var directory = getHome(type); - if (Files.notExists(directory)) { - return ImmutableLinkedList.empty(); - } - - try (var walker = Files.walk(directory, 1).sorted(Comparator.comparing(this::getLastModifiedTime))) { - return cachedUuids = walker.map(this::parsePathAsId) - .flatMap(Optional::stream) - .collect(Collectors.toCollection(LinkedList::new)); - } catch (IOException exception) { - return ImmutableLinkedList.empty(); + try (var walker = Files.walk(directory, 1).sorted(Comparator.comparing(this::getLastModifiedTime))) { + return cachedUuids = walker.map(this::parsePathAsId) + .flatMap(Optional::stream) + .collect(Collectors.toCollection(LinkedList::new)); + } catch (IOException exception) { + return ImmutableLinkedList.empty(); + } } } @@ -94,17 +93,23 @@ public LinkedList listPhoneNumbers(ClientType type) { return new ImmutableLinkedList<>(cachedPhoneNumbers); } - var directory = getHome(type); - if (Files.notExists(directory)) { - return ImmutableLinkedList.empty(); - } + synchronized (phoneNumbersLock) { + if (cachedPhoneNumbers != null) { + return new ImmutableLinkedList<>(cachedPhoneNumbers); + } - try (var walker = Files.walk(directory, 1).sorted(Comparator.comparing(this::getLastModifiedTime))) { - return cachedPhoneNumbers = walker.map(this::parsePathAsPhoneNumber) - .flatMap(Optional::stream) - .collect(Collectors.toCollection(LinkedList::new)); - } catch (IOException exception) { - return ImmutableLinkedList.empty(); + var directory = getHome(type); + if (Files.notExists(directory)) { + return ImmutableLinkedList.empty(); + } + + try (var walker = Files.walk(directory, 1).sorted(Comparator.comparing(this::getLastModifiedTime))) { + return cachedPhoneNumbers = walker.map(this::parsePathAsPhoneNumber) + .flatMap(Optional::stream) + .collect(Collectors.toCollection(LinkedList::new)); + } catch (IOException exception) { + return ImmutableLinkedList.empty(); + } } } @@ -139,13 +144,14 @@ public CompletableFuture serializeKeys(Keys keys, boolean async) { cachedUuids.add(keys.uuid()); } - var outputFile = getSessionFile(keys.clientType(), keys.uuid().toString(), KEYS_NAME); + var keysName = "keys" + fileExtension(); + var outputFile = getSessionFile(keys.clientType(), keys.uuid().toString(), keysName); if (async) { - return CompletableFuture.runAsync(() -> writeFile(KeysSpec.encode(keys), KEYS_NAME, outputFile)) + return CompletableFuture.runAsync(() -> writeFile(encodeKeys(keys), keysName, outputFile)) .exceptionallyAsync(this::onError); } - writeFile(KeysSpec.encode(keys), KEYS_NAME, outputFile); + writeFile(encodeKeys(keys), keysName, outputFile); return CompletableFuture.completedFuture(null); } @@ -171,8 +177,9 @@ public CompletableFuture serializeStore(Store store, boolean async) { .flatMap(Arrays::stream) .toArray(CompletableFuture[]::new); var result = CompletableFuture.allOf(dependableFutures).thenRunAsync(() -> { - var storePath = getSessionFile(store, STORE_NAME); - writeFile(StoreSpec.encode(store), STORE_NAME, storePath); + var storeName = "store" + fileExtension(); + var storePath = getSessionFile(store, storeName); + writeFile(encodeStore(store), storeName, storePath); }); if (async) { return result; @@ -194,9 +201,9 @@ private CompletableFuture serializeChatAsync(Store store, Chat chat) { return CompletableFuture.completedFuture(null); } - var fileName = CHAT_PREFIX + chat.jid().user() + ".proto"; + var fileName = CHAT_PREFIX + chat.jid().user() + fileExtension(); var outputFile = getSessionFile(store, fileName); - return CompletableFuture.runAsync(() -> writeFile(ChatSpec.encode(chat), fileName, outputFile)) + return CompletableFuture.runAsync(() -> writeFile(encodeChat(chat), fileName, outputFile)) .exceptionallyAsync(this::onError); } @@ -214,18 +221,19 @@ private CompletableFuture[] serializeNewslettersAsync(Store store) { } private CompletableFuture serializeNewsletterAsync(Store store, Newsletter newsletter) { - var fileName = NEWSLETTER_PREFIX + newsletter.jid().user() + ".proto"; + var fileName = NEWSLETTER_PREFIX + newsletter.jid().user() + fileExtension(); var outputFile = getSessionFile(store, fileName); - return CompletableFuture.runAsync(() -> writeFile(NewsletterSpec.encode(newsletter), fileName, outputFile)); + return CompletableFuture.runAsync(() -> writeFile(encodeNewsletter(newsletter), fileName, outputFile)); } private void writeFile(byte[] object, String fileName, Path outputFile) { try { - Files.createDirectories(outputFile.getParent()); var tempFile = Files.createTempFile(fileName, ".tmp"); + Files.createDirectories(tempFile.getParent()); try (var tempFileOutputStream = new GZIPOutputStream(Files.newOutputStream(tempFile))) { tempFileOutputStream.write(object); } + Files.createDirectories(outputFile.getParent()); Files.move(tempFile, outputFile, StandardCopyOption.REPLACE_EXISTING); } catch (IOException exception) { throw new UncheckedIOException("Cannot write file", exception); @@ -268,7 +276,7 @@ public Optional deserializeKeys(ClientType type, long phoneNumber) { private Optional deserializeKeysFromId(ClientType type, String id) { var path = getSessionFile(type, id, "keys.proto"); try (var input = new GZIPInputStream(Files.newInputStream(path))) { - return Optional.of(KeysSpec.decode(input.readAllBytes())); + return Optional.of(decodeKeys(input.readAllBytes())); } catch (IOException exception) { return Optional.empty(); } @@ -314,7 +322,7 @@ private Optional deserializeStoreFromId(ClientType type, String id) { } try (var input = new GZIPInputStream(Files.newInputStream(path))) { - return Optional.of(StoreSpec.decode(input.readAllBytes())); + return Optional.of(decodeStore(input.readAllBytes())); } catch (IOException exception) { return Optional.empty(); } @@ -363,6 +371,9 @@ private void attributeStoreContextInfo(Store store, ContextInfo contextInfo) { contextInfo.quotedMessageChatJid() .flatMap(store::findChatByJid) .ifPresent(contextInfo::setQuotedMessageChat); + contextInfo.quotedMessageSenderJid() + .flatMap(store::findContactByJid) + .ifPresent(contextInfo::setQuotedMessageSender); } private CompletableFuture handleStoreFile(Store store, Path entry) { @@ -453,9 +464,11 @@ private void linkToUuid(ClientType type, UUID uuid, String string) { private void deserializeChat(Store store, Path chatFile) { try (var input = new GZIPInputStream(Files.newInputStream(chatFile))) { - var chat = ChatSpec.decode(input.readAllBytes()); + var chat = decodeChat(input.readAllBytes()); for (var message : chat.messages()) { message.messageInfo().setChat(chat); + store.findContactByJid(message.messageInfo().senderJid()) + .ifPresent(message.messageInfo()::setSender); } store.addChatDirect(chat); } catch (IOException exception) { @@ -471,7 +484,7 @@ private Chat rescueChat(Path entry) { } var chatName = entry.getFileName().toString() .replaceFirst(CHAT_PREFIX, "") - .replace(".proto", ""); + .replace(fileExtension(), ""); return new ChatBuilder() .jid(Jid.of(chatName)) .build(); @@ -479,7 +492,7 @@ private Chat rescueChat(Path entry) { private void deserializeNewsletter(Store store, Path newsletterFile) { try (var input = new GZIPInputStream(Files.newInputStream(newsletterFile))) { - var newsletter = NewsletterSpec.decode(input.readAllBytes()); + var newsletter = decodeNewsletter(input.readAllBytes()); for (var message : newsletter.messages()) { message.setNewsletter(newsletter); } @@ -497,7 +510,7 @@ private Newsletter rescueNewsletter(Path entry) { } var newsletterName = entry.getFileName().toString() .replaceFirst(CHAT_PREFIX, "") - .replace(".proto", ""); + .replace(fileExtension(), ""); return new Newsletter(Jid.of(newsletterName), null, null, null); } @@ -538,7 +551,7 @@ private Path getSessionFile(ClientType clientType, String uuid, String fileName) private static class ImmutableLinkedList extends LinkedList { @SuppressWarnings({"rawtypes", "unchecked"}) private static final ImmutableLinkedList EMPTY = new ImmutableLinkedList(new LinkedList()); - + private final LinkedList delegate; @SuppressWarnings("unchecked") diff --git a/src/main/java/it/auties/whatsapp/controller/builtin/JsonControllerSerializer.java b/src/main/java/it/auties/whatsapp/controller/builtin/JsonControllerSerializer.java new file mode 100644 index 000000000..d3a051fe8 --- /dev/null +++ b/src/main/java/it/auties/whatsapp/controller/builtin/JsonControllerSerializer.java @@ -0,0 +1,85 @@ +package it.auties.whatsapp.controller.builtin; + +import it.auties.whatsapp.controller.ControllerSerializer; +import it.auties.whatsapp.controller.Keys; +import it.auties.whatsapp.controller.Store; +import it.auties.whatsapp.model.chat.Chat; +import it.auties.whatsapp.model.newsletter.Newsletter; +import it.auties.whatsapp.util.Json; + +import java.nio.file.Path; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +public class JsonControllerSerializer extends FileControllerSerializer { + private static final Path DEFAULT_SERIALIZER_PATH = Path.of(System.getProperty("user.home") + "/.cobalt/"); + private static final Map serializers = new ConcurrentHashMap<>(); + static { + serializers.put(DEFAULT_SERIALIZER_PATH, new JsonControllerSerializer(DEFAULT_SERIALIZER_PATH)); + } + + public static ControllerSerializer ofDefaultPath() { + return Objects.requireNonNull(serializers.get(DEFAULT_SERIALIZER_PATH)); + } + + public static ControllerSerializer of(Path baseDirectory) { + var known = serializers.get(baseDirectory); + if(known != null) { + return known; + } + + var result = new JsonControllerSerializer(baseDirectory); + serializers.put(baseDirectory, result); + return result; + } + + private JsonControllerSerializer(Path baseDirectory) { + super(baseDirectory); + } + + @Override + String fileExtension() { + return ".json"; + } + + @Override + byte[] encodeKeys(Keys keys) { + return Json.writeValueAsBytes(keys); + } + + @Override + byte[] encodeStore(Store store) { + return Json.writeValueAsBytes(store); + } + + @Override + byte[] encodeChat(Chat chat) { + return Json.writeValueAsBytes(chat); + } + + @Override + byte[] encodeNewsletter(Newsletter newsletter) { + return Json.writeValueAsBytes(newsletter); + } + + @Override + Keys decodeKeys(byte[] keys) { + return Json.readValue(keys, Keys.class); + } + + @Override + Store decodeStore(byte[] store) { + return Json.readValue(store, Store.class); + } + + @Override + Chat decodeChat(byte[] chat) { + return Json.readValue(chat, Chat.class); + } + + @Override + Newsletter decodeNewsletter(byte[] newsletter) { + return Json.readValue(newsletter, Newsletter.class); + } +} diff --git a/src/main/java/it/auties/whatsapp/controller/builtin/ProtobufControllerSerializer.java b/src/main/java/it/auties/whatsapp/controller/builtin/ProtobufControllerSerializer.java new file mode 100644 index 000000000..3430ede7e --- /dev/null +++ b/src/main/java/it/auties/whatsapp/controller/builtin/ProtobufControllerSerializer.java @@ -0,0 +1,84 @@ +package it.auties.whatsapp.controller.builtin; + +import it.auties.whatsapp.controller.*; +import it.auties.whatsapp.model.chat.Chat; +import it.auties.whatsapp.model.chat.ChatSpec; +import it.auties.whatsapp.model.newsletter.Newsletter; +import it.auties.whatsapp.model.newsletter.NewsletterSpec; + +import java.nio.file.Path; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +public class ProtobufControllerSerializer extends FileControllerSerializer { + private static final Path DEFAULT_SERIALIZER_PATH = Path.of(System.getProperty("user.home") + "/.cobalt/"); + private static final Map serializers = new ConcurrentHashMap<>(); + static { + serializers.put(DEFAULT_SERIALIZER_PATH, new ProtobufControllerSerializer(DEFAULT_SERIALIZER_PATH)); + } + + public static ControllerSerializer ofDefaultPath() { + return Objects.requireNonNull(serializers.get(DEFAULT_SERIALIZER_PATH)); + } + + public static ControllerSerializer of(Path baseDirectory) { + var known = serializers.get(baseDirectory); + if(known != null) { + return known; + } + + var result = new ProtobufControllerSerializer(baseDirectory); + serializers.put(baseDirectory, result); + return result; + } + + private ProtobufControllerSerializer(Path baseDirectory) { + super(baseDirectory); + } + + @Override + String fileExtension() { + return ".proto"; + } + + @Override + byte[] encodeKeys(Keys keys) { + return KeysSpec.encode(keys); + } + + @Override + byte[] encodeStore(Store store) { + return StoreSpec.encode(store); + } + + @Override + byte[] encodeChat(Chat chat) { + return ChatSpec.encode(chat); + } + + @Override + byte[] encodeNewsletter(Newsletter newsletter) { + return NewsletterSpec.encode(newsletter); + } + + @Override + Keys decodeKeys(byte[] keys) { + return KeysSpec.decode(keys); + } + + @Override + Store decodeStore(byte[] store) { + return StoreSpec.decode(store); + } + + @Override + Chat decodeChat(byte[] chat) { + return ChatSpec.decode(chat); + } + + @Override + Newsletter decodeNewsletter(byte[] newsletter) { + return NewsletterSpec.decode(newsletter); + } +} diff --git a/src/main/java/it/auties/whatsapp/crypto/AesGcm.java b/src/main/java/it/auties/whatsapp/crypto/AesGcm.java index d7b3d7e78..b204fe8fe 100644 --- a/src/main/java/it/auties/whatsapp/crypto/AesGcm.java +++ b/src/main/java/it/auties/whatsapp/crypto/AesGcm.java @@ -11,7 +11,7 @@ public final class AesGcm { private static final int TAG_LENGTH = 128; - private static byte[] cipher(byte[] iv, byte[] input, byte[] key, byte[] additionalData, boolean encrypt) { + private static byte[] cipher(byte[] iv, byte[] input, int offset, int length, byte[] key, byte[] additionalData, boolean encrypt) { try { var cipher = Cipher.getInstance("AES/GCM/NoPadding"); var keySpec = new SecretKeySpec(key, "AES"); @@ -20,9 +20,9 @@ private static byte[] cipher(byte[] iv, byte[] input, byte[] key, byte[] additio if(additionalData != null) { cipher.updateAAD(additionalData); } - var outputLength = cipher.getOutputSize(input.length); + var outputLength = cipher.getOutputSize(length); var output = new byte[outputLength]; - var outputOffset = cipher.update(input, 0, input.length, output, 0); + var outputOffset = cipher.update(input, offset, length, output, 0); cipher.doFinal(output, outputOffset); return output; } catch (Throwable throwable) { @@ -47,25 +47,38 @@ public static byte[] encrypt(long iv, byte[] input, byte[] key) { } public static byte[] encrypt(long iv, byte[] input, byte[] key, byte[] additionalData) { - return cipher(toIv(iv), input, key, additionalData, true); + return cipher(toIv(iv), input, 0, input.length, key, additionalData, true); } public static byte[] encrypt(byte[] iv, byte[] input, byte[] key, byte[] additionalData) { - return cipher(iv, input, key, additionalData, true); + return cipher(iv, input, 0, input.length, key, additionalData, true); } public static byte[] encrypt(byte[] iv, byte[] input, byte[] key) { - return cipher(iv, input, key, null, true); + return cipher(iv, input, 0, input.length, key, null, true); } public static byte[] decrypt(long iv, byte[] input, byte[] key) { - return decrypt(iv, input, key, null); + return decrypt(iv, input, 0, input.length, key); + } + + public static byte[] decrypt(long iv, byte[] input, int offset, int length, byte[] key) { + return decrypt(iv, input, offset, length, key, null); } public static byte[] decrypt(long iv, byte[] input, byte[] key, byte[] additionalData) { - return cipher(toIv(iv), input, key, additionalData, false); + return decrypt(iv, input, 0, input.length, key, additionalData); + } + + public static byte[] decrypt(long iv, byte[] input, int offset, int length, byte[] key, byte[] additionalData) { + return cipher(toIv(iv), input, offset, length, key, additionalData, false); } + public static byte[] decrypt(byte[] iv, byte[] input, byte[] key, byte[] additionalData) { - return cipher(iv, input, key, additionalData, false); + return decrypt(iv, input, 0, input.length, key, additionalData); + } + + public static byte[] decrypt(byte[] iv, byte[] input, int offset, int length, byte[] key, byte[] additionalData) { + return cipher(iv, input, offset, length, key, additionalData, false); } } diff --git a/src/main/java/it/auties/whatsapp/crypto/GroupCipher.java b/src/main/java/it/auties/whatsapp/crypto/GroupCipher.java index 3f09bd0c8..2caa658a1 100644 --- a/src/main/java/it/auties/whatsapp/crypto/GroupCipher.java +++ b/src/main/java/it/auties/whatsapp/crypto/GroupCipher.java @@ -5,14 +5,14 @@ import it.auties.whatsapp.model.signal.sender.SenderKeyName; import it.auties.whatsapp.model.signal.sender.SenderKeyState; import it.auties.whatsapp.model.signal.sender.SenderMessageKey; -import it.auties.whatsapp.util.Specification.Signal; +import it.auties.whatsapp.util.SignalConstants; import java.util.NoSuchElementException; public record GroupCipher(SenderKeyName name, Keys keys) { public CipheredMessageResult encrypt(byte[] data) { if (data == null) { - return new CipheredMessageResult(null, Signal.UNAVAILABLE); + return new CipheredMessageResult(null, SignalConstants.UNAVAILABLE); } var currentState = keys.findSenderKeyByName(name).firstState(); @@ -21,7 +21,7 @@ public CipheredMessageResult encrypt(byte[] data) { var senderKeyMessage = new SenderKeyMessage(currentState.id(), messageKey.iteration(), ciphertext, currentState.signingKey().privateKey()); var next = currentState.chainKey().next(); currentState.setChainKey(next); - return new CipheredMessageResult(senderKeyMessage.serialized(), Signal.SKMSG); + return new CipheredMessageResult(senderKeyMessage.serialized(), SignalConstants.SKMSG); } public byte[] decrypt(byte[] data) { diff --git a/src/main/java/it/auties/whatsapp/crypto/Hkdf.java b/src/main/java/it/auties/whatsapp/crypto/Hkdf.java index abbed2a4b..9631b04be 100644 --- a/src/main/java/it/auties/whatsapp/crypto/Hkdf.java +++ b/src/main/java/it/auties/whatsapp/crypto/Hkdf.java @@ -9,7 +9,7 @@ import java.security.GeneralSecurityException; import java.util.Arrays; -import static it.auties.whatsapp.util.Specification.Signal.KEY_LENGTH; +import static it.auties.whatsapp.util.SignalConstants.KEY_LENGTH; public final class Hkdf { private static final int ITERATION_START_OFFSET = 1; // v3 diff --git a/src/main/java/it/auties/whatsapp/crypto/SessionBuilder.java b/src/main/java/it/auties/whatsapp/crypto/SessionBuilder.java index 2c506cd3d..70069b085 100644 --- a/src/main/java/it/auties/whatsapp/crypto/SessionBuilder.java +++ b/src/main/java/it/auties/whatsapp/crypto/SessionBuilder.java @@ -8,7 +8,7 @@ import it.auties.whatsapp.model.signal.message.SignalPreKeyMessage; import it.auties.whatsapp.model.signal.session.*; import it.auties.whatsapp.util.Bytes; -import it.auties.whatsapp.util.Specification.Signal; +import it.auties.whatsapp.util.SignalConstants; import it.auties.whatsapp.util.Validate; import java.nio.charset.StandardCharsets; @@ -30,7 +30,7 @@ public void createOutgoing(int id, byte[] identityKey, SignalSignedKeyPair signe preKey == null ? null : preKey.keyPair().signalPublicKey(), signedPreKey.keyPair().signalPublicKey(), id, - Signal.CURRENT_VERSION + SignalConstants.CURRENT_VERSION ); var pendingPreKey = new SessionPreKey( preKey == null ? null : preKey.id(), @@ -113,13 +113,13 @@ public void createIncoming(Session session, SignalPreKeyMessage message) { } var preKeyPair = keys.findPreKeyById(message.preKeyId()) .orElse(null); - var signedPreKeyPair = keys.findSignedKeyPairById(message.signedPreKeyId()) - .orElseThrow(() -> new NoSuchElementException("Cannot find signed pre key with id %s".formatted(message.signedPreKeyId()))); + var signedKeyPair = keys.findSignedKeyPairById(message.signedPreKeyId()) + .orElseThrow(() -> new NoSuchElementException("Cannot find signed key with id %s".formatted(message.signedPreKeyId()))); session.closeCurrentState(); var nextState = createState( false, preKeyPair != null ? preKeyPair.toGenericKeyPair() : null, - signedPreKeyPair == null ? null : signedPreKeyPair.toGenericKeyPair(), + signedKeyPair.toGenericKeyPair(), message.identityKey(), message.baseKey(), null, diff --git a/src/main/java/it/auties/whatsapp/crypto/SessionCipher.java b/src/main/java/it/auties/whatsapp/crypto/SessionCipher.java index c05241225..1d25c4b4e 100644 --- a/src/main/java/it/auties/whatsapp/crypto/SessionCipher.java +++ b/src/main/java/it/auties/whatsapp/crypto/SessionCipher.java @@ -12,7 +12,7 @@ import it.auties.whatsapp.model.signal.session.SessionChain; import it.auties.whatsapp.model.signal.session.SessionState; import it.auties.whatsapp.util.Bytes; -import it.auties.whatsapp.util.Specification.Signal; +import it.auties.whatsapp.util.SignalConstants; import it.auties.whatsapp.util.Validate; import java.nio.charset.StandardCharsets; @@ -22,12 +22,12 @@ import java.util.function.Supplier; import static it.auties.curve25519.Curve25519.sharedKey; -import static it.auties.whatsapp.util.Specification.Signal.*; +import static it.auties.whatsapp.util.SignalConstants.*; public record SessionCipher(SessionAddress address, Keys keys) { public CipheredMessageResult encrypt(byte[] data) { if (data == null) { - return new CipheredMessageResult(null, Signal.UNAVAILABLE); + return new CipheredMessageResult(null, SignalConstants.UNAVAILABLE); } var currentState = loadSession().currentState() .orElseThrow(() -> new NoSuchElementException("Missing session for address %s".formatted(address))); @@ -46,7 +46,7 @@ public CipheredMessageResult encrypt(byte[] data) { } private String getMessageType(SessionState currentState) { - return currentState.hasPreKey() ? Signal.PKMSG : Signal.MSG; + return currentState.hasPreKey() ? SignalConstants.PKMSG : SignalConstants.MSG; } private byte[] encrypt(SessionState state, SessionChain chain, byte[] key, byte[] encrypted) { @@ -183,12 +183,12 @@ private void calculateRatchet(SignalMessage message, SessionState state, boolean } private Session loadSession() { - return loadSession(() -> keys.findSessionByAddress(new SessionAddress(address.name(), 0))); + return loadSession(null); } private Session loadSession(Supplier> defaultSupplier) { return keys.findSessionByAddress(address) - .or(defaultSupplier) + .or(defaultSupplier == null ? Optional::empty : defaultSupplier) .orElseThrow(() -> new NoSuchElementException("Missing session for: %s".formatted(address))); } } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/exception/RequestException.java b/src/main/java/it/auties/whatsapp/exception/RequestException.java index b2e948926..716e92011 100644 --- a/src/main/java/it/auties/whatsapp/exception/RequestException.java +++ b/src/main/java/it/auties/whatsapp/exception/RequestException.java @@ -4,11 +4,7 @@ * This exception is thrown when a request cannot be sent to Whatsapp's socket */ public class RequestException extends RuntimeException { - public RequestException(String message, Throwable cause) { - super(message, cause); - } - - public RequestException(Throwable cause) { - super(cause); + public RequestException(String message) { + super(message, null); } } diff --git a/src/main/java/it/auties/whatsapp/socket/AppStateHandler.java b/src/main/java/it/auties/whatsapp/implementation/AppStateHandler.java similarity index 98% rename from src/main/java/it/auties/whatsapp/socket/AppStateHandler.java rename to src/main/java/it/auties/whatsapp/implementation/AppStateHandler.java index f6558669f..3f468390e 100644 --- a/src/main/java/it/auties/whatsapp/socket/AppStateHandler.java +++ b/src/main/java/it/auties/whatsapp/implementation/AppStateHandler.java @@ -1,4 +1,4 @@ -package it.auties.whatsapp.socket; +package it.auties.whatsapp.implementation; import it.auties.whatsapp.api.ClientType; import it.auties.whatsapp.crypto.AesCbc; @@ -79,13 +79,14 @@ private CompletableFuture sendPush(Jid jid, List patches, bo .toMap(); var sync = Node.of("sync", syncAttributes, body); return socketHandler.sendQuery("set", "w:sync:app:state", sync) + .thenAcceptAsync(this::parseSyncRequest) + .thenRunAsync(() -> onPush(jid, requests, readPatches)) .whenCompleteAsync((result, error) -> { pushSemaphore.release(); if(error != null) { Exceptions.rethrow(error); } - }) - .thenRunAsync(() -> onPush(jid, requests, readPatches)); + }); }catch (Throwable throwable) { return CompletableFuture.failedFuture(throwable); } @@ -340,10 +341,7 @@ private List parseSyncRequest(Node node) { private Optional parseSync(Node sync) { var name = PatchType.of(sync.attributes().getString("name")); - var type = sync.attributes().getString("type"); - if (Objects.equals(type, "error")) { - return Optional.empty(); - } + Validate.isTrue(!sync.attributes().hasValue("type", "error"), "App state sync failed"); var more = sync.attributes().getBoolean("has_more_patches"); var snapshotSync = sync.findNode("snapshot") .flatMap(this::decodeSnapshot) @@ -399,7 +397,7 @@ private void onSetting(Setting setting) { } case PushNameSettings pushNameSettings -> { var oldName = socketHandler.store().name(); - socketHandler.updateUserName(pushNameSettings.name(), oldName); + socketHandler.onUserChanged(pushNameSettings.name(), oldName); } case UnarchiveChatsSettings unarchiveChatsSettings -> { var settingValue = unarchiveChatsSettings.unarchiveChats(); @@ -562,7 +560,7 @@ private byte[][] getSyncMutationMac(PatchSync patch) { return patch.mutations() .stream() .map(mutation -> mutation.record().value().blob()) - .map(entry -> Arrays.copyOfRange(entry, entry.length - Specification.Signal.KEY_LENGTH, entry.length)) + .map(entry -> Arrays.copyOfRange(entry, entry.length - SignalConstants.KEY_LENGTH, entry.length)) .toArray(byte[][]::new); } @@ -603,8 +601,8 @@ private Optional decodeMutation(Jid jid, RecordSync.Operation op return Optional.empty(); } var blob = sync.value().blob(); - var encryptedBlob = Arrays.copyOfRange(blob, 0, blob.length - Specification.Signal.KEY_LENGTH); - var encryptedMac = Arrays.copyOfRange(blob, blob.length - Specification.Signal.KEY_LENGTH, blob.length); + var encryptedBlob = Arrays.copyOfRange(blob, 0, blob.length - SignalConstants.KEY_LENGTH); + var encryptedMac = Arrays.copyOfRange(blob, blob.length - SignalConstants.KEY_LENGTH, blob.length); Validate.isTrue(!socketHandler.store().checkPatchMacs() || Arrays.equals(encryptedMac, generateMac(operation, encryptedBlob, sync.keyId().id(), mutationKeys.get().macKey())), "decode_mutation", HmacValidationException.class); var result = AesCbc.decrypt(encryptedBlob, mutationKeys.get().encKey()); @@ -617,11 +615,11 @@ private Optional decodeMutation(Jid jid, RecordSync.Operation op private byte[] generateMac(RecordSync.Operation operation, byte[] data, byte[] keyId, byte[] key) { var keyData = Bytes.concat(operation.content(), keyId); - var last = new byte[Specification.Signal.MAC_LENGTH]; + var last = new byte[SignalConstants.MAC_LENGTH]; last[last.length - 1] = (byte) keyData.length; var total = Bytes.concat(keyData, data, last); var sha512 = Hmac.calculateSha512(total, key); - return Arrays.copyOfRange(sha512, 0, Specification.Signal.KEY_LENGTH); + return Arrays.copyOfRange(sha512, 0, SignalConstants.KEY_LENGTH); } private byte[] generateSnapshotMac(byte[] ltHash, long version, PatchType patchType, byte[] key) { diff --git a/src/main/java/it/auties/whatsapp/socket/AuthHandler.java b/src/main/java/it/auties/whatsapp/implementation/AuthHandler.java similarity index 77% rename from src/main/java/it/auties/whatsapp/socket/AuthHandler.java rename to src/main/java/it/auties/whatsapp/implementation/AuthHandler.java index 78c67afbb..a56c90299 100644 --- a/src/main/java/it/auties/whatsapp/socket/AuthHandler.java +++ b/src/main/java/it/auties/whatsapp/implementation/AuthHandler.java @@ -1,17 +1,15 @@ -package it.auties.whatsapp.socket; +package it.auties.whatsapp.implementation; import it.auties.curve25519.Curve25519; -import it.auties.protobuf.exception.ProtobufDeserializationException; import it.auties.whatsapp.api.ClientType; import it.auties.whatsapp.model.mobile.CountryLocale; import it.auties.whatsapp.model.mobile.PhoneNumber; import it.auties.whatsapp.model.signal.auth.*; import it.auties.whatsapp.model.sync.HistorySyncConfigBuilder; import it.auties.whatsapp.util.Bytes; -import it.auties.whatsapp.util.Specification; +import it.auties.whatsapp.util.SignalConstants; import java.util.NoSuchElementException; -import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ThreadLocalRandom; @@ -23,64 +21,37 @@ class AuthHandler { this.socketHandler = socketHandler; } - protected CompletableFuture login(byte[] message) { + protected CompletableFuture login(byte[] message) { try { - var serverHello = readHandshake(message); - if (serverHello.isEmpty()) { - return CompletableFuture.completedFuture(false); - } - - var handshake = new SocketHandshake(socketHandler.keys(), getPrologueData()); + var serverHandshake = HandshakeMessageSpec.decode(message); + var serverHello = serverHandshake.serverHello(); + var handshake = new SocketHandshake(socketHandler.keys(), SocketHandshake.getPrologue(socketHandler.store().clientType())); handshake.updateHash(socketHandler.keys().ephemeralKeyPair().publicKey()); - handshake.updateHash(serverHello.get().ephemeral()); - var sharedEphemeral = Curve25519.sharedKey(serverHello.get().ephemeral(), socketHandler.keys().ephemeralKeyPair().privateKey()); + handshake.updateHash(serverHello.ephemeral()); + var sharedEphemeral = Curve25519.sharedKey(serverHello.ephemeral(), socketHandler.keys().ephemeralKeyPair().privateKey()); handshake.mixIntoKey(sharedEphemeral); - var decodedStaticText = handshake.cipher(serverHello.get().staticText(), false); + var decodedStaticText = handshake.cipher(serverHello.staticText(), false); var sharedStatic = Curve25519.sharedKey(decodedStaticText, socketHandler.keys().ephemeralKeyPair().privateKey()); handshake.mixIntoKey(sharedStatic); - handshake.cipher(serverHello.get().payload(), false); + handshake.cipher(serverHello.payload(), false); var encodedKey = handshake.cipher(socketHandler.keys().noiseKeyPair().publicKey(), true); - var sharedPrivate = Curve25519.sharedKey(serverHello.get().ephemeral(), socketHandler.keys().noiseKeyPair().privateKey()); + var sharedPrivate = Curve25519.sharedKey(serverHello.ephemeral(), socketHandler.keys().noiseKeyPair().privateKey()); handshake.mixIntoKey(sharedPrivate); var payload = createUserClientPayload(); var encodedPayload = handshake.cipher(ClientPayloadSpec.encode(payload), true); var clientFinish = new ClientFinish(encodedKey, encodedPayload); - var handshakeMessage = new HandshakeMessageBuilder() + var clientHandshake = new HandshakeMessageBuilder() .clientFinish(clientFinish) .build(); - return sendHandshake(handshake, handshakeMessage); + return socketHandler.sendBinaryWithNoResponse(HandshakeMessageSpec.encode(clientHandshake), false).thenRunAsync(() -> { + socketHandler.keys().clearReadWriteKey(); + handshake.finish(); + }); } catch (Throwable throwable) { return CompletableFuture.failedFuture(throwable); } } - private byte[] getPrologueData() { - return switch (socketHandler.store().clientType()) { - case WEB -> Specification.Whatsapp.WEB_PROLOGUE; - case MOBILE -> Specification.Whatsapp.MOBILE_PROLOGUE; - }; - } - - private Optional readHandshake(byte[] message) { - try { - var handshakeMessage = HandshakeMessageSpec.decode(message); - return Optional.ofNullable(handshakeMessage.serverHello()); - } catch (ProtobufDeserializationException exception) { - return Optional.empty(); - } - } - - private CompletableFuture sendHandshake(SocketHandshake handshake, HandshakeMessage handshakeMessage) { - return socketHandler.sendBinaryWithNoResponse(HandshakeMessageSpec.encode(handshakeMessage), false) - .thenApplyAsync(result -> onHandshakeSent(handshake)); - } - - private boolean onHandshakeSent(SocketHandshake handshake) { - socketHandler.keys().clearReadWriteKey(); - handshake.finish(); - return true; - } - private WebInfo createWebInfo() { return new WebInfoBuilder() .webSubPlatform(WebInfo.Platform.WEB_BROWSER) @@ -97,11 +68,13 @@ private UserAgent createUserAgent() { .osVersion(mobile ? socketHandler.store().device().osVersion().toString() : null) .manufacturer(mobile ? socketHandler.store().device().manufacturer() : null) .device(mobile ? socketHandler.store().device().model().replaceAll("_", " ") : null) - .osBuildNumber(mobile ? socketHandler.store().device().osVersion().toString() : null) - .phoneId(mobile ? socketHandler.keys().fdid() : null) + .osBuildNumber(mobile ? socketHandler.store().device().osBuildNumber() : null) + .phoneId(mobile ? socketHandler.keys().fdid().toUpperCase() : null) .releaseChannel(socketHandler.store().releaseChannel()) .localeLanguageIso6391(socketHandler.store().locale().map(CountryLocale::languageValue).orElse("en")) .localeCountryIso31661Alpha2(socketHandler.store().locale().map(CountryLocale::languageCode).orElse("US")) + .deviceType(UserAgent.DeviceType.PHONE) + .deviceModelType(socketHandler.store().device().modelId()) .build(); } @@ -115,9 +88,9 @@ private ClientPayload createUserClientPayload() { .orElseThrow(() -> new NoSuchElementException("Missing phone number for mobile registration")); yield new ClientPayloadBuilder() .username(phoneNumber) - .passive(false) - .userAgent(agent) + .passive(true) .pushName(socketHandler.store().name()) + .userAgent(agent) .sessionId(ThreadLocalRandom.current().nextInt(100_000_000, 1_000_000_000)) .shortConnect(true) .connectType(ClientPayload.ClientPayloadConnectType.WIFI_UNKNOWN) @@ -125,6 +98,7 @@ yield new ClientPayloadBuilder() .dnsSource(getDnsSource()) .connectAttemptCount(0) .device(0) + .oc(false) .build(); } case WEB -> { @@ -166,7 +140,7 @@ private CompanionRegistrationData createRegisterData() { var companion = new CompanionRegistrationDataBuilder() .buildHash(socketHandler.store().version().toHash()) .eRegid(socketHandler.keys().encodedRegistrationId()) - .eKeytype(Bytes.intToBytes(Specification.Signal.KEY_TYPE, 1)) + .eKeytype(Bytes.intToBytes(SignalConstants.KEY_TYPE, 1)) .eIdent(socketHandler.keys().identityKeyPair().publicKey()) .eSkeyId(socketHandler.keys().signedKeyPair().encodedId()) .eSkeyVal(socketHandler.keys().signedKeyPair().keyPair().publicKey()) diff --git a/src/main/java/it/auties/whatsapp/socket/MessageHandler.java b/src/main/java/it/auties/whatsapp/implementation/MessageHandler.java similarity index 95% rename from src/main/java/it/auties/whatsapp/socket/MessageHandler.java rename to src/main/java/it/auties/whatsapp/implementation/MessageHandler.java index 0ab21b01f..0d6ddc834 100644 --- a/src/main/java/it/auties/whatsapp/socket/MessageHandler.java +++ b/src/main/java/it/auties/whatsapp/implementation/MessageHandler.java @@ -1,4 +1,4 @@ -package it.auties.whatsapp.socket; +package it.auties.whatsapp.implementation; import it.auties.linkpreview.LinkPreview; import it.auties.linkpreview.LinkPreviewMatch; @@ -57,24 +57,26 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; import java.util.stream.Stream; import static it.auties.whatsapp.api.ErrorHandler.Location.*; -import static it.auties.whatsapp.util.Specification.Signal.*; +import static it.auties.whatsapp.util.SignalConstants.*; class MessageHandler { private static final int HISTORY_SYNC_TIMEOUT = 25; + private static final int MAX_MESSAGE_RETRIES = 5; private final SocketHandler socketHandler; private final Map> pastParticipantsQueue; private final Map> devicesCache; + private final Map retries; private final Set historyCache; private final EnumSet historySyncTypes; private final ReentrantLock lock; - private CompletableFuture historySyncTask; + private ScheduledFuture historySyncTask; protected MessageHandler(SocketHandler socketHandler) { this.socketHandler = socketHandler; @@ -83,6 +85,7 @@ protected MessageHandler(SocketHandler socketHandler) { this.historyCache = ConcurrentHashMap.newKeySet(); this.historySyncTypes = EnumSet.noneOf(Type.class); this.lock = new ReentrantLock(true); + this.retries = new ConcurrentHashMap<>(); } protected CompletableFuture encode(MessageSendRequest request) { @@ -97,7 +100,11 @@ private CompletableFuture encodeChatMessage(MessageSendRequest.Chat reques .thenComposeAsync(ignored -> { try { lock.lock(); - return request.peer() || isConversation(request.info()) ? encodeConversation(request) : encodeGroup(request); + if (request.peer() || isConversation(request.info())) { + return encodeConversation(request); + }else { + return encodeGroup(request); + } } finally { lock.unlock(); } @@ -170,7 +177,8 @@ private CompletableFuture attributeTextMessage(TextMessage textMessage) { } return LinkPreview.createPreviewAsync(textMessage.text()) - .thenComposeAsync(result -> attributeTextMessage(textMessage, result.orElse(null))); + .thenComposeAsync(result -> attributeTextMessage(textMessage, result.orElse(null))) + .exceptionallyAsync(error -> null); } private CompletableFuture attributeTextMessage(TextMessage textMessage, LinkPreviewMatch match) { @@ -255,7 +263,6 @@ private CompletableFuture attributePollCreationMessage(ChatMessageInfo inf info.setPollAdditionalMetadata(metadata); info.message().deviceInfo().ifPresentOrElse(deviceInfo -> deviceInfo.setMessageSecret(pollEncryptionKey), () -> { var deviceInfo = new DeviceContextInfoBuilder() - .deviceListMetadataVersion(2) .messageSecret(pollEncryptionKey) .build(); var message = info.message().withDeviceInfo(deviceInfo); @@ -851,7 +858,7 @@ private void decodeNewsletterMessage(Node messageNode, Node plainTextNode, JidPr newsletter.get().addMessage(result.get()); if(notify) { - socketHandler.onNewsletterMessage(result.get()); + socketHandler.onNewMessage(result.get()); } } catch (Throwable throwable) { socketHandler.handleFailure(MESSAGE, throwable); @@ -949,55 +956,68 @@ private void decodeChatMessage(Node infoNode, Node messageNode, String businessN } var key = keyBuilder.id(id).build(); if (Objects.equals(key.senderJid().orElse(null), socketHandler.store().jid().orElse(null))) { - sendEncMessageReceipt(infoNode, id, key.chatJid(), key.senderJid().orElse(null), key.fromMe()); + sendEncMessageSuccessReceipt(infoNode, id, key.chatJid(), key.senderJid().orElse(null), key.fromMe()); return; } - if (messageNode == null) { - socketHandler.handleFailure(MESSAGE, new RuntimeException("Cannot decode message(id: %s, from: %s)".formatted(id, from))); - sendEncMessageReceipt(infoNode, id, key.chatJid(), key.senderJid().orElse(null), key.fromMe()); + var messageContainer = decodeChatMessageContainer(messageNode, from, participant); + if(messageContainer.hasError()) { + sendEncMessageRetryReceipt(infoNode, key, participant, timestamp, messageContainer.error()); return; } - var type = messageNode.attributes().getRequiredString("type"); - var encodedMessage = messageNode.contentAsBytes().orElse(null); - var decodedMessage = decodeMessageBytes(type, encodedMessage, from, participant); - if (decodedMessage.hasError()) { - socketHandler.handleFailure(MESSAGE, new RuntimeException("Cannot decode message(id: %s, from: %s): %s".formatted(id, from, decodedMessage.error().getMessage()))); - sendEncMessageReceipt(infoNode, id, key.chatJid(), key.senderJid().orElse(null), key.fromMe()); - return; - } - - var messageContainer = Bytes.bytesToMessage(decodedMessage.message()).unbox(); var info = messageBuilder.key(key) .broadcast(key.chatJid().hasServer(JidServer.BROADCAST)) .pushName(pushName) .status(MessageStatus.DELIVERED) .businessVerifiedName(businessName) .timestampSeconds(timestamp) - .message(messageContainer) + .message(messageContainer.message()) .build(); attributeMessageReceipt(info); attributeChatMessage(info); saveMessage(info, notify); socketHandler.onReply(info); - sendEncMessageReceipt(infoNode, id, key.chatJid(), key.senderJid().orElse(null), key.fromMe()); + sendEncMessageSuccessReceipt(infoNode, id, key.chatJid(), key.senderJid().orElse(null), key.fromMe()); } catch (Throwable throwable) { socketHandler.handleFailure(MESSAGE, throwable); - } finally { + }finally { lock.unlock(); } } - private void sendEncMessageReceipt(Node infoNode, String id, Jid chatJid, Jid senderJid, boolean fromMe) { - var participant = fromMe && senderJid == null ? chatJid : senderJid; - var category = infoNode.attributes().getString("category"); - var receiptType = getReceiptType(category, fromMe); + private void sendEncMessageRetryReceipt(Node infoNode, ChatMessageKey key, Jid participant, long timestamp, Throwable error) { + socketHandler.sendMessageAck(key.chatJid(), infoNode).thenComposeAsync(ignored -> { + var counter = retries.getOrDefault(key.id(), 0); + if(counter >= MAX_MESSAGE_RETRIES) { + socketHandler.handleFailure(MESSAGE, error); + return CompletableFuture.completedFuture(null); + } + + retries.put(key.id(), counter + 1); + return socketHandler.sendRetryReceipt(timestamp, key.chatJid(), participant, key.id(), 1); + }); + } + + private MessageDecodeResult decodeChatMessageContainer(Node messageNode, Jid from, Jid participant) { + if (messageNode == null) { + return new MessageDecodeResult(null, new RuntimeException("Message is not available")); + } + + var type = messageNode.attributes().getRequiredString("type"); + var encodedMessage = messageNode.contentAsBytes().orElse(null); + return decodeMessageBytes(type, encodedMessage, from, participant); + } + + private void sendEncMessageSuccessReceipt(Node infoNode, String id, Jid chatJid, Jid senderJid, boolean fromMe) { socketHandler.sendMessageAck(chatJid, infoNode).thenComposeAsync(ignored -> { if(!socketHandler.store().automaticMessageReceipts()) { return CompletableFuture.completedFuture(null); } + var participant = fromMe && senderJid == null ? chatJid : senderJid; + var category = infoNode.attributes().getString("category"); + var receiptType = getReceiptType(category, fromMe); return socketHandler.sendReceipt(chatJid, participant, List.of(id), receiptType); }); } @@ -1046,7 +1066,9 @@ private MessageDecodeResult decodeMessageBytes(String type, byte[] encodedMessag } default -> throw new IllegalArgumentException("Unsupported encoded message type: %s".formatted(type)); }; - return new MessageDecodeResult(result, null); + var message = Bytes.bytesToMessage(result) + .unbox(); + return new MessageDecodeResult(message, null); } catch (Throwable throwable) { return new MessageDecodeResult(null, throwable); } @@ -1067,9 +1089,10 @@ private void attributeMessageReceipt(ChatMessageInfo info) { } private void saveMessage(ChatMessageInfo info, boolean notify) { - if (info.message().content() instanceof SenderKeyDistributionMessage distributionMessage) { - handleDistributionMessage(distributionMessage, info.senderJid()); - } + info.message() + .senderKeyDistributionMessage() + .ifPresent(keyDistributionMessage -> handleDistributionMessage(keyDistributionMessage, info.senderJid())); + if (info.chatJid().type() == JidType.STATUS) { socketHandler.store().addStatus(info); socketHandler.onNewStatus(info); @@ -1293,12 +1316,11 @@ private void handleConversationsNotifications(HistorySync history) { } private void scheduleHistorySyncTimeout() { - var executor = CompletableFuture.delayedExecutor(HISTORY_SYNC_TIMEOUT, TimeUnit.SECONDS, Thread::startVirtualThread); if (historySyncTask != null && !historySyncTask.isDone()) { historySyncTask.cancel(true); } - this.historySyncTask = CompletableFuture.runAsync(this::onForcedHistorySyncCompletion, executor); + this.historySyncTask = socketHandler.scheduleDelayed(this::onForcedHistorySyncCompletion, HISTORY_SYNC_TIMEOUT); } private void onForcedHistorySyncCompletion() { @@ -1482,7 +1504,7 @@ protected void dispose() { historySyncTypes.clear(); } - private record MessageDecodeResult(byte[] message, Throwable error) { + private record MessageDecodeResult(MessageContainer message, Throwable error) { public boolean hasError() { return error != null; } diff --git a/src/main/java/it/auties/whatsapp/socket/SocketHandler.java b/src/main/java/it/auties/whatsapp/implementation/SocketHandler.java similarity index 82% rename from src/main/java/it/auties/whatsapp/socket/SocketHandler.java rename to src/main/java/it/auties/whatsapp/implementation/SocketHandler.java index 8759ba18d..8c70ec8fc 100644 --- a/src/main/java/it/auties/whatsapp/socket/SocketHandler.java +++ b/src/main/java/it/auties/whatsapp/implementation/SocketHandler.java @@ -1,12 +1,12 @@ -package it.auties.whatsapp.socket; +package it.auties.whatsapp.implementation; import it.auties.whatsapp.api.*; import it.auties.whatsapp.api.ErrorHandler.Location; -import it.auties.whatsapp.binary.BinaryDecoder; -import it.auties.whatsapp.binary.BinaryEncoder; import it.auties.whatsapp.controller.Keys; import it.auties.whatsapp.controller.Store; import it.auties.whatsapp.crypto.AesGcm; +import it.auties.whatsapp.io.BinaryDecoder; +import it.auties.whatsapp.io.BinaryEncoder; import it.auties.whatsapp.listener.Listener; import it.auties.whatsapp.model.action.Action; import it.auties.whatsapp.model.business.BusinessCategory; @@ -14,7 +14,10 @@ import it.auties.whatsapp.model.chat.*; import it.auties.whatsapp.model.contact.Contact; import it.auties.whatsapp.model.contact.ContactStatus; -import it.auties.whatsapp.model.info.*; +import it.auties.whatsapp.model.info.ChatMessageInfo; +import it.auties.whatsapp.model.info.ChatMessageInfoBuilder; +import it.auties.whatsapp.model.info.MessageIndexInfo; +import it.auties.whatsapp.model.info.MessageInfo; import it.auties.whatsapp.model.jid.Jid; import it.auties.whatsapp.model.jid.JidProvider; import it.auties.whatsapp.model.jid.JidServer; @@ -38,12 +41,10 @@ import it.auties.whatsapp.model.sync.PatchRequest; import it.auties.whatsapp.model.sync.PatchType; import it.auties.whatsapp.model.sync.PrimaryFeature; +import it.auties.whatsapp.util.Bytes; import it.auties.whatsapp.util.Clock; import it.auties.whatsapp.util.Exceptions; -import it.auties.whatsapp.util.Specification; -import java.io.ByteArrayOutputStream; -import java.io.DataOutputStream; import java.io.IOException; import java.io.UncheckedIOException; import java.net.SocketException; @@ -65,46 +66,34 @@ public class SocketHandler implements SocketListener { private static final Set connectedPhoneNumbers = ConcurrentHashMap.newKeySet(); private static final Set connectedAlias = ConcurrentHashMap.newKeySet(); - private SocketSession session; + public static boolean isConnected(UUID uuid) { + return connectedUuids.contains(uuid); + } - private final Whatsapp whatsapp; + public static boolean isConnected(long phoneNumber) { + return connectedPhoneNumbers.contains(phoneNumber); + } - private final AuthHandler authHandler; + public static boolean isConnected(String id) { + return connectedAlias.contains(id); + } + private SocketSession session; + private final Whatsapp whatsapp; + private final AuthHandler authHandler; private final StreamHandler streamHandler; - private final MessageHandler messageHandler; - private final AppStateHandler appStateHandler; - private final ErrorHandler errorHandler; - private final AtomicLong requestsCounter; private final ScheduledExecutorService scheduler; + private final List> scheduledTasks; private final Semaphore writeSemaphore; - private volatile SocketState state; - private volatile CompletableFuture loginFuture; - private Keys keys; - private Store store; - private Thread shutdownHook; - - public static boolean isConnected(UUID uuid) { - return connectedUuids.contains(uuid); - } - - public static boolean isConnected(long phoneNumber) { - return connectedPhoneNumbers.contains(phoneNumber); - } - - public static boolean isConnected(String id) { - return connectedAlias.contains(id); - } - public SocketHandler(Whatsapp whatsapp, Store store, Keys keys, ErrorHandler errorHandler, WebVerificationHandler webVerificationHandler) { this.whatsapp = whatsapp; this.store = store; @@ -117,6 +106,7 @@ public SocketHandler(Whatsapp whatsapp, Store store, Keys keys, ErrorHandler err this.errorHandler = Objects.requireNonNullElse(errorHandler, ErrorHandler.toTerminal()); this.requestsCounter = new AtomicLong(); this.scheduler = Executors.newScheduledThreadPool(0, Thread.ofVirtual().factory()); + this.scheduledTasks = new CopyOnWriteArrayList<>(); this.writeSemaphore = new Semaphore(1, true); } @@ -125,11 +115,20 @@ private void onShutdown(boolean reconnect) { keys.dispose(); store.dispose(); } + + killScheduledTasks(); if (!reconnect) { dispose(); } } + private void killScheduledTasks() { + for(var scheduledTask : scheduledTasks) { + scheduledTask.cancel(true); + } + scheduledTasks.clear(); + } + protected void onSocketEvent(SocketEvent event) { callListenersAsync(listener -> { listener.onSocketEvent(whatsapp, event); @@ -176,24 +175,23 @@ protected void addToKnownConnections() { } @Override - public void onMessage(byte[] message) { - if (state != SocketState.CONNECTED && state != SocketState.RESTORE) { - authHandler.login(message) - .thenAcceptAsync(this::onConnectionCreated) - .exceptionallyAsync(throwable -> handleFailure(LOGIN, throwable)); + public void onMessage(byte[] message, int length) { + if (state == SocketState.WAITING || state == SocketState.RECONNECTING || state == SocketState.PAUSED) { + handshake(message.length != length ? Arrays.copyOfRange(message, 0, length) : message); // for now copy array return; } - var readKey = keys.readKey(); - if (readKey.isEmpty()) { - return; + if(state == SocketState.HANDSHAKE) { + setState(SocketState.CONNECTED); } - var decipheredMessage = decipherMessage(message, readKey.get()); - if(decipheredMessage == null) { + var readKey = keys.readKey(); + if (readKey.isEmpty()) { return; } + var iv = keys.nextReadCounter(true); + var decipheredMessage = AesGcm.decrypt(iv, message, 0, length, readKey.get()); try(var decoder = new BinaryDecoder(decipheredMessage)) { var node = decoder.decode(); onNodeReceived(node); @@ -204,27 +202,27 @@ public void onMessage(byte[] message) { } } - private void onConnectionCreated(Boolean result) { - if (result == null || !result) { - return; - } + private void handshake(byte[] message) { + authHandler.login(message).whenCompleteAsync((result, throwable) -> { + if (throwable == null || state == SocketState.RECONNECTING) { + setState(SocketState.HANDSHAKE); + return; + } - setState(SocketState.CONNECTED); + handleFailure(LOGIN, throwable); + }); } - private byte[] decipherMessage(byte[] message, byte[] readKey) { - try { - return AesGcm.decrypt(keys.nextReadCounter(true), message, readKey); - } catch (Throwable throwable) { - return null; - } - } + private void onNodeReceived(Node node) { + var caller = store.findPendingRequest(node.id()); + if(caller.isPresent() && caller.get().body() instanceof Node callerNode && callerNode.hasNode("ping")) { + return; + } - private void onNodeReceived(Node deciphered) { callListenersAsync(listener -> { - listener.onNodeReceived(whatsapp, deciphered); - listener.onNodeReceived(deciphered); + listener.onNodeReceived(whatsapp, node); + listener.onNodeReceived(node); }); } @@ -241,17 +239,17 @@ public void onClose() { @Override public void onError(Throwable throwable) { - if (isIgnorableSocketError(throwable)) { + if(!(throwable instanceof SocketException socketException)) { + onSocketEvent(SocketEvent.ERROR); + handleFailure(UNKNOWN, throwable); + return; + } + + if(state() == SocketState.RECONNECTING || state() == SocketState.DISCONNECTED) { return; } - onSocketEvent(SocketEvent.ERROR); - handleFailure(UNKNOWN, throwable); - } - private boolean isIgnorableSocketError(Throwable throwable) { - return throwable instanceof SocketException socketException - && Objects.equals(socketException.getMessage(), "Socket closed") - && (state() == SocketState.RECONNECTING || state() == SocketState.DISCONNECTED); + handleFailure(STREAM, socketException); } public CompletableFuture sendNode(Node node) { @@ -260,7 +258,7 @@ public CompletableFuture sendNode(Node node) { public CompletableFuture sendNode(Node node, Function filter) { if (node.id() == null) { - node.attributes().put("id", store.initializationTimeStamp() + "-" + requestsCounter.incrementAndGet()); + node.attributes().put("id", HexFormat.of().formatHex(Bytes.random(6))); } return sendRequest(SocketRequest.of(node, filter), false, true); @@ -281,30 +279,26 @@ private CompletableFuture sendRequest(SocketRequest request, boolean prolo return CompletableFuture.completedFuture(Node.of("error", Map.of("closed", true))); } - var byteArrayOutputStream = new ByteArrayOutputStream(); - try(var dataOutputStream = new DataOutputStream(byteArrayOutputStream)) { + var scheduledRelease = false; + try { writeSemaphore.acquire(); var ciphered = encryptRequest(request); - if(prologue) { - dataOutputStream.write(switch (store.clientType()) { - case WEB -> Specification.Whatsapp.WEB_PROLOGUE; - case MOBILE -> Specification.Whatsapp.MOBILE_PROLOGUE; - }); - } - dataOutputStream.writeInt(ciphered.length >> 16); - dataOutputStream.writeShort(65535 & ciphered.length); - dataOutputStream.write(ciphered); - session.sendBinary(byteArrayOutputStream.toByteArray()).whenComplete((result, error) -> { + var message = Bytes.concat( + prologue ? SocketHandshake.getPrologue(store.clientType()) : null, + Bytes.intToBytes(ciphered.length >> 16, 4), + Bytes.intToBytes(65535 & ciphered.length, 2), + ciphered + ); + var future = session.sendBinary(message); + scheduledRelease = true; + future.whenCompleteAsync((result, error) -> { writeSemaphore.release(); if(request.body() instanceof Node body) { onNodeSent(body); } if(error != null) { - if(response) { - request.future().complete(Node.of("error", Map.of("closed", true))); // Prevent NPEs all over the place - } - + request.future().completeExceptionally(error); return; } @@ -317,6 +311,10 @@ private CompletableFuture sendRequest(SocketRequest request, boolean prolo }); return request.future(); }catch (Throwable throwable) { + if(!scheduledRelease) { + writeSemaphore.release(); + } + return CompletableFuture.failedFuture(throwable); } } @@ -384,48 +382,56 @@ public CompletableFuture disconnect(DisconnectReason reason) { setState(newState); keys.clearReadWriteKey(); return switch (reason) { - case DISCONNECTED -> { - if (session != null) { - session.disconnect(); - } - yield CompletableFuture.completedFuture(null); - } - case RECONNECTING -> { - store.resolveAllPendingRequests(); - if (session != null) { - session.disconnect(); - } - yield connect(); - } - case LOGGED_OUT, BANNED -> { - store.deleteSession(); - store.resolveAllPendingRequests(); - if (session != null) { - session.disconnect(); - } - yield CompletableFuture.completedFuture(null); - } - case RESTORE -> { - store.deleteSession(); - store.resolveAllPendingRequests(); - var oldListeners = new ArrayList<>(store.listeners()); - if (session != null) { - session.disconnect(); - } - var uuid = UUID.randomUUID(); - var number = store.phoneNumber() - .map(PhoneNumber::number) - .orElse(null); - var result = store.serializer() - .newStoreKeysPair(uuid, number, store.alias(), store.clientType()); - this.keys = result.keys(); - this.store = result.store(); - store.addListeners(oldListeners); - yield connect(); - } + case DISCONNECTED -> handleDisconnection(); + case RECONNECTING -> handleReconnection(); + case LOGGED_OUT, BANNED -> handleLoggedOut(); + case RESTORE -> handleRestore(); }; } + private CompletableFuture handleRestore() { + store.deleteSession(); + store.resolveAllPendingRequests(); + var oldListeners = new ArrayList<>(store.listeners()); + if (session != null) { + session.disconnect(); + } + var uuid = UUID.randomUUID(); + var number = store.phoneNumber() + .map(PhoneNumber::number) + .orElse(null); + var result = store.serializer() + .newStoreKeysPair(uuid, number, store.alias(), store.clientType()); + this.keys = result.keys(); + this.store = result.store(); + store.addListeners(oldListeners); + return connect(); + } + + private CompletableFuture handleLoggedOut() { + store.deleteSession(); + store.resolveAllPendingRequests(); + return handleDisconnection(); + } + + private CompletableFuture handleReconnection() { + store.resolveAllPendingRequests(); + if (session != null) { + session.disconnect(); + } + + return connect(); + } + + private CompletableFuture handleDisconnection() { + store.resolveAllPendingRequests(); + if (session != null) { + session.disconnect(); + } + + return CompletableFuture.completedFuture(null); + } + public CompletableFuture pushPatch(PatchRequest request) { var jid = store.jid().orElseThrow(() -> new IllegalStateException("The session isn't connected")); return appStateHandler.push(jid, List.of(request)); @@ -499,6 +505,10 @@ private SocketRequest createRequest(Node node, Function filter, b } private void onNodeSent(Node node) { + if (node.hasNode("ping")) { + return; + } + callListenersAsync(listener -> { listener.onNodeSent(whatsapp, node); listener.onNodeSent(node); @@ -671,8 +681,12 @@ public GroupMetadata parseGroupMetadata(Node node) { var founder = node.attributes() .getOptionalJid("creator"); var policies = new HashMap(); - policies.put(SEND_MESSAGES, ChatSettingPolicy.of(node.hasNode("restrict"))); policies.put(EDIT_GROUP_INFO, ChatSettingPolicy.of(node.hasNode("announce"))); + policies.put(SEND_MESSAGES, ChatSettingPolicy.of(node.hasNode("restrict"))); + var addParticipantsMode = node.findNode("member_add_mode") + .flatMap(Node::contentAsString) + .orElse(null); + policies.put(ADD_PARTICIPANTS, ChatSettingPolicy.of(Objects.equals(addParticipantsMode, "admin_add"))); policies.put(APPROVE_PARTICIPANTS, ChatSettingPolicy.of(node.hasNode("membership_approval_mode"))); var description = node.findNode("description") .flatMap(parent -> parent.findNode("body")) @@ -682,7 +696,7 @@ public GroupMetadata parseGroupMetadata(Node node) { .flatMap(attributes -> attributes.getOptionalString("id")); var community = node.findNode("parent") .isPresent(); - var openCommunity = node.findNode("parent") + var openCommunity = community && node.findNode("parent") .filter(entry -> entry.attributes().hasValue("default_membership_approval_mode", "request_required")) .isEmpty(); var ephemeral = node.findNode("ephemeral") @@ -711,6 +725,25 @@ public CompletableFuture sendQuery(Jid to, String method, String category, return sendQuery(null, to, method, category, null, body); } + public CompletableFuture sendRetryReceipt(long nodeTimestamp, Jid chatJid, Jid participantJid, String messageId, int retryCount) { + var retryAttributes = Attributes.of() + .put("count", 1) + .put("id", messageId) + .put("t", nodeTimestamp) + .put("v", 1) + .toMap(); + var retryNode = Node.of("retry", retryAttributes); + var registrationNode = Node.of("registration", keys.encodedRegistrationId()); + var receiptAttributes = Attributes.of() + .put("id", messageId) + .put("type", "retry") + .put("to", chatJid.withAgent(null)) + .put("participant", participantJid == null ? null : participantJid.withAgent(null), participantJid != null) + .toMap(); + var receipt = Node.of("receipt", receiptAttributes, retryNode, registrationNode); + return sendNodeWithNoResponse(receipt); + } + public CompletableFuture sendReceipt(Jid jid, Jid participant, List messages, String type) { if (messages.isEmpty()) { return CompletableFuture.completedFuture(null); @@ -719,11 +752,11 @@ public CompletableFuture sendReceipt(Jid jid, Jid participant, List Objects.equals(type, "read") || Objects.equals(type, "read-self")) - .put("to", jid) + .put("to", jid.withAgent(null)) .put("type", type, Objects::nonNull); if (Objects.equals(type, "sender") && jid.hasServer(JidServer.WHATSAPP)) { - attributes.put("recipient", jid); - attributes.put("to", participant); + attributes.put("recipient", jid.withAgent(null)); + attributes.put("to", participant.withAgent(null)); } var receipt = Node.of("receipt", attributes.toMap(), toMessagesNode(messages)); @@ -770,7 +803,6 @@ protected void onMetadata(Map properties) { }); } - protected void onMessageStatus(MessageInfo message) { callListenersAsync(listener -> { listener.onMessageStatus(whatsapp, message); @@ -785,14 +817,15 @@ protected void onUpdateChatPresence(ContactStatus status, Jid jid, Chat chat) { contact.get().setLastSeen(ZonedDateTime.now()); } + var provider = contact.isPresent() ? contact.get() : jid; chat.presences().put(jid, status); callListenersAsync(listener -> { - listener.onContactPresence(whatsapp, chat, jid, status); - listener.onContactPresence(chat, jid, status); + listener.onContactPresence(whatsapp, chat, provider); + listener.onContactPresence(chat, provider); }); } - protected void onNewMessage(ChatMessageInfo info) { + protected void onNewMessage(MessageInfo info) { callListenersAsync(listener -> { listener.onNewMessage(whatsapp, info); listener.onNewMessage(info); @@ -842,6 +875,11 @@ protected void onAction(Action action, MessageIndexInfo indexInfo) { } protected void onDisconnected(DisconnectReason reason) { + if(state == SocketState.WAITING || state == SocketState.HANDSHAKE) { + handleFailure(LOGIN, new RuntimeException("Cannot login: no response from Whatsapp")); + return; + } + if (reason != DisconnectReason.RECONNECTING) { connectedUuids.remove(store.uuid()); store.phoneNumber() @@ -893,13 +931,6 @@ protected void onNewsletters() { }); } - protected void onNewsletterMessage(NewsletterMessageInfo messageInfo) { - callListenersAsync(listener -> { - listener.onNewMessage(whatsapp, messageInfo); - listener.onNewMessage(messageInfo); - }); - } - protected void onStatus() { callListenersAsync(listener -> { listener.onStatus(whatsapp, store().status()); @@ -955,22 +986,19 @@ protected void onUserAboutChanged(String newAbout, String oldAbout) { } public void onUserPictureChanged(URI newPicture, URI oldPicture) { - callListenersAsync(listener -> { - listener.onProfilePictureChanged(whatsapp, oldPicture, newPicture); - listener.onProfilePictureChanged(oldPicture, newPicture); - }); + callListenersAsync(listener -> store().jid() + .flatMap(store()::findContactByJid) + .ifPresent(selfJid -> { + listener.onProfilePictureChanged(whatsapp, selfJid); + listener.onProfilePictureChanged(selfJid); + })); } - public void updateUserName(String newName, String oldName) { + public void onUserChanged(String newName, String oldName) { if (oldName != null && !Objects.equals(newName, oldName)) { - var wasOnline = store().online(); - sendNodeWithNoResponse(Node.of("presence", Map.of("name", oldName, "type", "unavailable"))); - sendNodeWithNoResponse(Node.of("presence", Map.of("name", newName, "type", "available"))); - if(!wasOnline) { - sendNodeWithNoResponse(Node.of("presence", Map.of("name", oldName, "type", "unavailable"))); - } onUserNameChanged(newName, oldName); } + var self = store.jid() .orElseThrow(() -> new IllegalStateException("The session isn't connected")) .toSimpleJid(); @@ -1039,8 +1067,8 @@ public void onPrivacySettingChanged(PrivacySettingEntry oldEntry, PrivacySetting }); } - protected void querySessionsForcefully(Jid jid) { - messageHandler.querySessions(List.of(jid), true); + protected CompletableFuture querySessionsForcefully(Jid jid) { + return messageHandler.querySessions(List.of(jid), true); } private void dispose() { @@ -1049,6 +1077,7 @@ private void dispose() { messageHandler.dispose(); appStateHandler.dispose(); scheduler.shutdownNow(); + confirmConnection(); } protected T handleFailure(Location location, Throwable throwable) { @@ -1118,13 +1147,28 @@ void confirmConnection() { loginFuture.complete(null); } - protected ScheduledFuture scheduleAtFixedInterval(Runnable command, long initialDelay, long period) { - return scheduler.scheduleAtFixedRate(command, initialDelay, period, TimeUnit.SECONDS); + @SuppressWarnings("SameParameterValue") + protected void scheduleAtFixedInterval(Runnable command, long initialDelay, long period) { + var result = scheduler.scheduleAtFixedRate(command, initialDelay, period, TimeUnit.SECONDS); + scheduledTasks.add(result); + } + + protected ScheduledFuture scheduleDelayed(Runnable command, long delay) { + var result = scheduler.schedule(command, delay, TimeUnit.SECONDS); + scheduledTasks.add(result); + return result; } protected void sendPing() { sendQuery("get", "w:p", Node.of("ping")) .thenRunAsync(() -> onSocketEvent(SocketEvent.PING)) - .exceptionallyAsync(throwable -> handleFailure(STREAM, throwable)); + .exceptionallyAsync(throwable -> { + disconnect(DisconnectReason.RECONNECTING); + return null; + }); + } + + public CompletableFuture updateBusinessCertificate(String newName) { + return streamHandler.updateBusinessCertificate(newName); } } diff --git a/src/main/java/it/auties/whatsapp/socket/SocketHandshake.java b/src/main/java/it/auties/whatsapp/implementation/SocketHandshake.java similarity index 60% rename from src/main/java/it/auties/whatsapp/socket/SocketHandshake.java rename to src/main/java/it/auties/whatsapp/implementation/SocketHandshake.java index c0c515ad7..4915e0d0a 100644 --- a/src/main/java/it/auties/whatsapp/socket/SocketHandshake.java +++ b/src/main/java/it/auties/whatsapp/implementation/SocketHandshake.java @@ -1,15 +1,31 @@ -package it.auties.whatsapp.socket; +package it.auties.whatsapp.implementation; +import it.auties.whatsapp.api.ClientType; import it.auties.whatsapp.controller.Keys; import it.auties.whatsapp.crypto.AesGcm; import it.auties.whatsapp.crypto.Hkdf; import it.auties.whatsapp.crypto.Sha256; +import it.auties.whatsapp.io.BinaryTokens; import it.auties.whatsapp.util.Bytes; -import it.auties.whatsapp.util.Specification; +import java.nio.charset.StandardCharsets; import java.util.Arrays; class SocketHandshake { + private static final byte[] NOISE_PROTOCOL = "Noise_XX_25519_AESGCM_SHA256\0\0\0\0".getBytes(StandardCharsets.UTF_8); + private static final byte[] WHATSAPP_VERSION_HEADER = "WA".getBytes(StandardCharsets.UTF_8); + private static final byte[] WEB_VERSION = new byte[]{6, BinaryTokens.DICTIONARY_VERSION}; + private static final byte[] WEB_PROLOGUE = Bytes.concat(WHATSAPP_VERSION_HEADER, WEB_VERSION); + private static final byte[] MOBILE_VERSION = new byte[]{5, BinaryTokens.DICTIONARY_VERSION}; + private static final byte[] MOBILE_PROLOGUE = Bytes.concat(WHATSAPP_VERSION_HEADER, MOBILE_VERSION); + + public static byte[] getPrologue(ClientType clientType) { + return switch (clientType) { + case WEB -> WEB_PROLOGUE; + case MOBILE -> MOBILE_PROLOGUE; + }; + } + private final Keys keys; private byte[] hash; private byte[] salt; @@ -18,9 +34,9 @@ class SocketHandshake { SocketHandshake(Keys keys, byte[] prologue) { this.keys = keys; - this.hash = Specification.Whatsapp.NOISE_PROTOCOL; - this.salt = Specification.Whatsapp.NOISE_PROTOCOL; - this.cryptoKey = Specification.Whatsapp.NOISE_PROTOCOL; + this.hash = NOISE_PROTOCOL; + this.salt = NOISE_PROTOCOL; + this.cryptoKey = NOISE_PROTOCOL; this.counter = 0; updateHash(prologue); } diff --git a/src/main/java/it/auties/whatsapp/socket/SocketListener.java b/src/main/java/it/auties/whatsapp/implementation/SocketListener.java similarity index 60% rename from src/main/java/it/auties/whatsapp/socket/SocketListener.java rename to src/main/java/it/auties/whatsapp/implementation/SocketListener.java index 8ad4fef4d..784888e5c 100644 --- a/src/main/java/it/auties/whatsapp/socket/SocketListener.java +++ b/src/main/java/it/auties/whatsapp/implementation/SocketListener.java @@ -1,9 +1,9 @@ -package it.auties.whatsapp.socket; +package it.auties.whatsapp.implementation; public interface SocketListener { void onOpen(SocketSession session); - void onMessage(byte[] message); + void onMessage(byte[] message, int length); void onClose(); diff --git a/src/main/java/it/auties/whatsapp/socket/SocketRequest.java b/src/main/java/it/auties/whatsapp/implementation/SocketRequest.java similarity index 78% rename from src/main/java/it/auties/whatsapp/socket/SocketRequest.java rename to src/main/java/it/auties/whatsapp/implementation/SocketRequest.java index 46a72b27b..eef30adbc 100644 --- a/src/main/java/it/auties/whatsapp/socket/SocketRequest.java +++ b/src/main/java/it/auties/whatsapp/implementation/SocketRequest.java @@ -1,7 +1,9 @@ -package it.auties.whatsapp.socket; +package it.auties.whatsapp.implementation; -import it.auties.whatsapp.binary.BinaryEncoder; +import it.auties.whatsapp.exception.RequestException; +import it.auties.whatsapp.io.BinaryEncoder; import it.auties.whatsapp.model.node.Node; +import it.auties.whatsapp.util.Exceptions; import java.io.IOException; import java.io.UncheckedIOException; @@ -13,18 +15,19 @@ public record SocketRequest(String id, Object body, CompletableFuture future, Function filter) { private static final int TIMEOUT = 60; - private static final int PING_TIMEOUT = 5; private SocketRequest(String id, Function filter, Object body) { this(id, body, futureOrTimeout(body), filter); } private static CompletableFuture futureOrTimeout(Object body) { - return new CompletableFuture().orTimeout(calculateTimeout(body), SECONDS); - } - - private static int calculateTimeout(Object body) { - return body instanceof Node node && node.hasNode("ping") ? PING_TIMEOUT : TIMEOUT; + var stacktraceProvider = Exceptions.current(); + return new CompletableFuture().orTimeout(TIMEOUT, SECONDS).exceptionally(throwable -> { + var error = new RequestException("Node timed out: " + body); + error.setStackTrace(stacktraceProvider.getStackTrace()); + error.addSuppressed(throwable); + throw error; + }); } static SocketRequest of(Node body, Function filter) { diff --git a/src/main/java/it/auties/whatsapp/implementation/SocketSession.java b/src/main/java/it/auties/whatsapp/implementation/SocketSession.java new file mode 100644 index 000000000..bfa0e7b48 --- /dev/null +++ b/src/main/java/it/auties/whatsapp/implementation/SocketSession.java @@ -0,0 +1,264 @@ +package it.auties.whatsapp.implementation; + +import it.auties.whatsapp.net.SocketClient; +import it.auties.whatsapp.net.WebSocketClient; + +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.concurrent.CompletableFuture; + +public abstract sealed class SocketSession permits SocketSession.WebSocketSession, SocketSession.RawSocketSession { + private static final String WEB_SOCKET_HOST = "web.whatsapp.com"; + private static final int WEB_SOCKET_PORT = 443; + private static final String WEB_SOCKET_PATH = "/ws/chat"; + private static final String MOBILE_SOCKET_HOST = "g.whatsapp.net"; + private static final int MOBILE_SOCKET_PORT = 443; + private static final int MESSAGE_LENGTH = 3; + + final URI proxy; + SocketListener listener; + + private SocketSession(URI proxy) { + this.proxy = proxy; + } + + CompletableFuture connect(SocketListener listener) { + this.listener = listener; + return CompletableFuture.completedFuture(null); + } + + abstract void disconnect(); + + public abstract CompletableFuture sendBinary(byte[] bytes); + + static SocketSession of(URI proxy, boolean webSocket) { + if (webSocket) { + return new WebSocketSession(proxy); + } + + return new RawSocketSession(proxy); + } + + public static final class WebSocketSession extends SocketSession implements WebSocketClient.Listener { + private WebSocketClient webSocket; + private final MessageOutputStream messageOutputStream; + + WebSocketSession(URI proxy) { + super(proxy); + this.messageOutputStream = new MessageOutputStream(); + } + + @Override + CompletableFuture connect(SocketListener listener) { + if (webSocket != null) { + return CompletableFuture.completedFuture(null); + } + + super.connect(listener); + try { + var sslContext = SSLContext.getInstance("TLSv1.3"); + sslContext.init(null, null, null); + var sslEngine = sslContext.createSSLEngine(WEB_SOCKET_HOST, WEB_SOCKET_PORT); + sslEngine.setUseClientMode(true); + this.webSocket = WebSocketClient.newSecureClient(sslEngine, proxy, this); + var endpoint = proxy != null ? InetSocketAddress.createUnresolved(WEB_SOCKET_HOST, WEB_SOCKET_PORT) : new InetSocketAddress(WEB_SOCKET_HOST, WEB_SOCKET_PORT); + return webSocket.connectAsync(endpoint, WEB_SOCKET_PATH) + .thenRunAsync(() -> listener.onOpen(this)); + }catch (Throwable throwable) { + return CompletableFuture.failedFuture(throwable); + } + } + + @Override + void disconnect() { + if (webSocket == null) { + return; + } + + webSocket.close(); + } + + @Override + public CompletableFuture sendBinary(byte[] bytes) { + if (webSocket == null) { + return CompletableFuture.completedFuture(null); + } + + return webSocket.sendBinary(ByteBuffer.wrap(bytes)); + } + + @Override + public void onClose(int statusCode, String reason) { + listener.onClose(); + this.webSocket = null; + } + + @Override + public void onMessage(ByteBuffer data, boolean last) { + try { + while (data.hasRemaining()) { + if(messageOutputStream.isReady()) { + if(data.remaining() < MESSAGE_LENGTH) { + break; + } + + var messageLength = (data.get() << 16) | Short.toUnsignedInt(data.getShort()); + if (messageLength < 0) { + disconnect(); + return; + } + + messageOutputStream.init(messageLength); + } + + var result = messageOutputStream.write(data); + if(result != null) { + listener.onMessage(result, messageOutputStream.length()); + } + } + } catch (Throwable throwable) { + listener.onError(throwable); + } + } + + private final static class MessageOutputStream { + private byte[] buffer; + private int position; + private int limit; + public void init(int limit) { + if(buffer == null || buffer.length < limit) { + this.buffer = new byte[limit]; + } + + this.limit = limit; + } + + public byte[] write(ByteBuffer input) { + if(!input.hasRemaining()) { + return null; + } + + var remaining = Math.min(limit - position, input.remaining()); + input.get(buffer, position, remaining); + position += remaining; + if (position != limit) { + return null; + } + + position = 0; + return buffer; + } + + public boolean isReady() { + return position == 0; + } + + public int length() { + return limit; + } + } + } + + static final class RawSocketSession extends SocketSession { + private volatile SocketClient socket; + + RawSocketSession(URI proxy) { + super(proxy); + } + + @Override + CompletableFuture connect(SocketListener listener) { + if (isOpen()) { + return CompletableFuture.completedFuture(null); + } + + super.connect(listener); + try { + this.socket = SocketClient.newPlainClient(proxy); + var address = proxy != null ? InetSocketAddress.createUnresolved(MOBILE_SOCKET_HOST, MOBILE_SOCKET_PORT) : new InetSocketAddress(MOBILE_SOCKET_HOST, MOBILE_SOCKET_PORT); + return socket.connectAsync(address).thenRunAsync(() -> { + listener.onOpen(RawSocketSession.this); + notifyNextMessage(); + }); + }catch (IOException exception) { + return CompletableFuture.failedFuture(exception); + } + } + + private void notifyNextMessage() { + if(socket == null) { + return; + } + + socket.readFullyAsync( + MESSAGE_LENGTH, + this::readNextMessageLength + ); + } + + private void readNextMessageLength(ByteBuffer lengthBuffer, Throwable error) { + if(error != null) { + disconnect(); + return; + } + + var messageLength = (lengthBuffer.get() << 16) | ((lengthBuffer.get() & 0xFF) << 8) | (lengthBuffer.get() & 0xFF); + if(messageLength < 0) { + disconnect(); + return; + } + + socket.readFullyAsync( + messageLength, + this::notifyNextMessage + ); + } + + private void notifyNextMessage(ByteBuffer messageBuffer, Throwable error) { + if(error != null) { + disconnect(); + return; + } + + try { + var message = messageBuffer.array(); + listener.onMessage(message, message.length); + }catch (Throwable throwable) { + listener.onError(throwable); + }finally { + notifyNextMessage(); + } + } + + @Override + void disconnect() { + try { + if (socket == null) { + return; + } + + listener.onClose(); + socket.close(); + this.socket = null; + } catch (Throwable ignored) { + // No need to handle this + } + } + + private boolean isOpen() { + return socket != null && socket.isConnected(); + } + + @Override + public CompletableFuture sendBinary(byte[] bytes) { + if (socket == null) { + return CompletableFuture.completedFuture(null); + } + + return socket.writeAsync(bytes); + } + } +} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/socket/SocketState.java b/src/main/java/it/auties/whatsapp/implementation/SocketState.java similarity index 77% rename from src/main/java/it/auties/whatsapp/socket/SocketState.java rename to src/main/java/it/auties/whatsapp/implementation/SocketState.java index 811cba594..a79ea81a5 100644 --- a/src/main/java/it/auties/whatsapp/socket/SocketState.java +++ b/src/main/java/it/auties/whatsapp/implementation/SocketState.java @@ -1,9 +1,10 @@ -package it.auties.whatsapp.socket; +package it.auties.whatsapp.implementation; import it.auties.whatsapp.api.DisconnectReason; public enum SocketState { WAITING, + HANDSHAKE, CONNECTED, DISCONNECTED, RECONNECTING, @@ -24,8 +25,8 @@ static SocketState of(DisconnectReason reason) { DisconnectReason toReason() { return switch (this) { - case WAITING, CONNECTED, RECONNECTING -> DisconnectReason.RECONNECTING; - case DISCONNECTED, PAUSED -> DisconnectReason.DISCONNECTED; + case CONNECTED, RECONNECTING -> DisconnectReason.RECONNECTING; + case WAITING, HANDSHAKE, DISCONNECTED, PAUSED -> DisconnectReason.DISCONNECTED; case LOGGED_OUT -> DisconnectReason.LOGGED_OUT; case RESTORE -> DisconnectReason.RESTORE; case BANNED -> DisconnectReason.BANNED; diff --git a/src/main/java/it/auties/whatsapp/socket/StreamHandler.java b/src/main/java/it/auties/whatsapp/implementation/StreamHandler.java similarity index 90% rename from src/main/java/it/auties/whatsapp/socket/StreamHandler.java rename to src/main/java/it/auties/whatsapp/implementation/StreamHandler.java index ae846c12b..bc7af5a79 100644 --- a/src/main/java/it/auties/whatsapp/socket/StreamHandler.java +++ b/src/main/java/it/auties/whatsapp/implementation/StreamHandler.java @@ -1,4 +1,4 @@ -package it.auties.whatsapp.socket; +package it.auties.whatsapp.implementation; import com.fasterxml.jackson.databind.ObjectMapper; import it.auties.curve25519.Curve25519; @@ -42,7 +42,10 @@ import it.auties.whatsapp.model.signal.keypair.SignalKeyPair; import it.auties.whatsapp.model.signal.keypair.SignalPreKeyPair; import it.auties.whatsapp.model.sync.PatchType; -import it.auties.whatsapp.util.*; +import it.auties.whatsapp.util.Bytes; +import it.auties.whatsapp.util.Clock; +import it.auties.whatsapp.util.Json; +import it.auties.whatsapp.util.Validate; import javax.crypto.Cipher; import javax.crypto.SecretKey; @@ -60,38 +63,44 @@ import java.util.Map.Entry; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; import static it.auties.whatsapp.api.ErrorHandler.Location.*; -import static it.auties.whatsapp.util.Specification.Signal.KEY_BUNDLE_TYPE; -import static it.auties.whatsapp.util.Specification.Whatsapp.ACCOUNT_SIGNATURE_HEADER; -import static it.auties.whatsapp.util.Specification.Whatsapp.DEVICE_WEB_SIGNATURE_HEADER; +import static it.auties.whatsapp.util.SignalConstants.KEY_BUNDLE_TYPE; class StreamHandler { + private static final byte[] DEVICE_WEB_SIGNATURE_HEADER = {6, 1}; private static final int REQUIRED_PRE_KEYS_SIZE = 5; private static final int WEB_PRE_KEYS_UPLOAD_CHUNK = 30; - private static final int MOBILE_PRE_KEYS_UPLOAD_CHUNK = 811; - private static final int PING_INTERVAL = 30; - private static final int MAX_ATTEMPTS = 5; + private static final int PING_INTERVAL = 20; + private static final int MAX_MESSAGE_RETRIES = 5; private static final int DEFAULT_NEWSLETTER_MESSAGES = 100; + private static final byte[][] CALL_RELAY = new byte[][]{ + new byte[]{-105, 99, -47, -29, 13, -106}, + new byte[]{-99, -16, -53, 62, 13, -106}, + new byte[]{-99, -16, -25, 62, 13, -106}, + new byte[]{-99, -16, -5, 62, 13, -106}, + new byte[]{-71, 60, -37, 62, 13, -106} + }; + private static final byte[] ACCOUNT_SIGNATURE_HEADER = {6, 0}; private final SocketHandler socketHandler; private final WebVerificationHandler webVerificationHandler; private final Map retries; private final AtomicReference lastLinkCodeKey; - private volatile ScheduledFuture pingFuture; - private volatile ScheduledFuture mediaConnectionFuture; + private final AtomicBoolean retryConnection; protected StreamHandler(SocketHandler socketHandler, WebVerificationHandler webVerificationHandler) { this.socketHandler = socketHandler; this.webVerificationHandler = webVerificationHandler; this.retries = new ConcurrentHashMap<>(); this.lastLinkCodeKey = new AtomicReference<>(); + this.retryConnection = new AtomicBoolean(false); } protected void digest(Node node) { @@ -204,31 +213,31 @@ private void onChatReceipt(Node node, Jid chatJid, ChatMessageInfo message) { }); message.setStatus(status); - if (Objects.equals(type.orElse(null), "retry")) { - sendMessageRetry(message); + if (!Objects.equals(type.orElse(null), "retry")) { + return; } + + acceptMessageRetry(message); } - private void sendMessageRetry(ChatMessageInfo message) { + private void acceptMessageRetry(ChatMessageInfo message) { if (!message.fromMe()) { return; } var attempts = retries.getOrDefault(message.id(), 0); - if (attempts > MAX_ATTEMPTS) { + if (attempts > MAX_MESSAGE_RETRIES) { return; } - try { + socketHandler.querySessionsForcefully(message.senderJid()).whenCompleteAsync((result, error) -> { var all = message.senderJid().device() == 0; - socketHandler.querySessionsForcefully(message.senderJid()); message.chat().ifPresent(Chat::clearParticipantsPreKeys); var recipients = all ? null : Set.of(message.senderJid()); var request = new MessageSendRequest.Chat(message, recipients, !all, false, null); socketHandler.sendMessage(request); - } finally { retries.put(message.id(), attempts + 1); - } + }); } private void updateReceipt(MessageStatus status, Chat chat, Contact participant, ChatMessageInfo message) { @@ -341,7 +350,7 @@ private void digestCallAck(Node node) { } private void sendRelay(Jid callCreator, String callId, Jid to) { - for (var value : Specification.Whatsapp.CALL_RELAY) { + for (var value : CALL_RELAY) { var te = Node.of("te", Map.of("latency", 33554440), value); var relay = Node.of("relaylatency", Map.of("call-creator", callCreator, "call-id", callId), te); socketHandler.sendNode(Node.of("call", Map.of("to", to), relay)); @@ -589,9 +598,29 @@ private void handleGroupNotification(Node node) { return; } + // TODO: Handle all cases + if (stubType.get() == ChatMessageInfo.StubType.GROUP_CHANGE_SUBJECT) { + onGroupSubjectChange(node); + } + handleGroupStubNotification(node, stubType.get()); } + private void onGroupSubjectChange(Node node) { + var subject = node.findNode("subject") + .flatMap(subjectNode -> subjectNode.attributes().getOptionalString("subject")) + .orElse(null); + if(subject == null) { + return; + } + + var fromJid = node.attributes() + .getRequiredJid("from"); + socketHandler.store() + .findChatByJid(fromJid) + .ifPresent(chat -> chat.setName(subject)); + } + private void handleGroupStubNotification(Node node, ChatMessageInfo.StubType stubType) { var timestamp = node.attributes().getLong("t"); var fromJid = node.attributes() @@ -850,7 +879,7 @@ private void digestError(Node node) { var statusCode = node.attributes().getInt("code"); switch (statusCode) { - case 403, 503 -> socketHandler.disconnect(DisconnectReason.BANNED); + case 403, 503 -> socketHandler.disconnect(retryConnection.getAndSet(true) ? DisconnectReason.BANNED : DisconnectReason.RECONNECTING); case 500 -> socketHandler.disconnect(DisconnectReason.LOGGED_OUT); case 401 -> handleStreamError(node); default -> node.children().forEach(error -> socketHandler.store().resolvePendingRequest(error, true)); @@ -895,7 +924,6 @@ private void digestSuccess(Node node) { configureApi().thenRunAsync(() -> { onRegistration(); onInitialInfo(); - notifyChatsAndNewsletters(true); }).exceptionallyAsync(throwable -> socketHandler.handleFailure(LOGIN, throwable)); }else { loggedInFuture.thenRunAsync(this::onInitialInfo); @@ -911,12 +939,13 @@ private void digestSuccess(Node node) { private CompletableFuture initSession() { return CompletableFuture.allOf( - scheduleMediaConnectionUpdate(0, null), + scheduleMediaConnectionUpdate(), updateSelfPresence(), queryInitial2fa(), queryInitialAboutPrivacy(), queryInitialPrivacySettings(), queryInitialDisappearingMode(), + sendInitialMetadata(), queryInitialBlockList(), updateUserAbout(false), updateUserPicture(false) @@ -931,12 +960,14 @@ private CompletableFuture configureApi() { ); case MOBILE -> CompletableFuture.allOf( acceptTermsOfService(), + setPushEndpoint(), setDefaultStatus(), resetMultiDevice(), setupGoogleCrypto(), setupRescueToken(), getInviteSender(), - preRegistrationAddRequests() + preRegistrationAddRequests(), + sendNumberMetadata() ); }; } @@ -953,6 +984,51 @@ private CompletableFuture setDefaultStatus() { return socketHandler.changeAbout("Hey there! I am using WhatsApp."); } + private CompletableFuture sendInitialMetadata() { + var wamBinary = "57414d0501010001200b800d086950686f6e652058800f0631362e372e34801109322e32342e352e373510152017502f15a1fc65206928830138790604387b060288eb0a036e616f88a513053231363233186b1818a71c88911e063230483234308879240431372e3018fb2e18ed3318ab3888fb3c09353735323538303733290c147602dce54645287fd941"; + var wamData = new String(HexFormat.of().parseHex(wamBinary)) + .replace("iPhone X", socketHandler.store().device().model().replaceAll("_", " ")) + .replace("2.24.5.75", socketHandler.store().version().toString()) + .getBytes(); + var addNode = Node.of("add", Map.of("t", Clock.nowSeconds()), wamData); + return socketHandler.sendQuery("set", "w:stats", addNode); + } + + private CompletableFuture sendNumberMetadata() { + var wamBinary = "57414d0501010001200b800d086950686f6e652058800f0631362e372e34801109322e32342e322e373110152017502fc8a2b265206928830138790604387b060288eb0a03636c6e186b1818a71c88911e063230483234308879240431372e3018fb2e18ed3318ab3888fb3c09353537393735393734290c147602705fefb1a86cd941"; + var wamData = new String(HexFormat.of().parseHex(wamBinary)) + .replace("iPhone X", socketHandler.store().device().model().replaceAll("_", " ")) + .replace("2.24.2.71", socketHandler.store().version().toString()) + .replace("557975974", String.valueOf(socketHandler.store().phoneNumber().orElseThrow().numberWithoutPrefix())) + .getBytes(); + var addNode = Node.of("add", Map.of("t", Clock.nowSeconds()), wamData); + return socketHandler.sendQuery("set", "w:stats", addNode) + .thenRun(() -> {}); + } + + private CompletableFuture setPushEndpoint() { + var configAttributes = Attributes.of() + .put("background_location", 1) + .put("call", "Opening.m4r") + .put("default", "note.m4r") + .put("groups", "node.m4r") + .put("id", HexFormat.of().formatHex(Bytes.random(32))) + .put("lc", "US") + .put("lg", "en") + .put("nse_call", 0) + .put("nse_read", 0) + .put("nse_ver", 2) + .put("pkey", Base64.getUrlEncoder().encodeToString(SignalKeyPair.random().publicKey())) + .put("platform", "apple") + .put("preview", 1) + .put("reg_push", 1) + .put("version", 2) + .put("voip", "35e178c41d2bd90b8db50c7a2684a38bf802e760cd1f2d7ff803d663412a9320") + .put("voip_payload_type", 2) + .toMap(); + return socketHandler.sendQuery("set", "urn:xmpp:whatsapp:push", Node.of("config", configAttributes)); + } + private CompletableFuture resetMultiDevice() { return socketHandler.sendQuery("set", "md", Node.of("remove-companion-device", Map.of("all", true, "reason", "user_initiated"))) .thenComposeAsync(ignored -> socketHandler.sendQuery("set", "w:sync:app:state", Node.of("delete_all_data"))); @@ -1053,9 +1129,9 @@ private void onGroupsQuery(Node result) { .forEach(socketHandler::handleGroupMetadata); } - private CompletableFuture setBusinessCertificate() { + protected CompletableFuture updateBusinessCertificate(String name) { var details = new BusinessVerifiedNameDetailsBuilder() - .name("") + .name(Objects.requireNonNullElse(name, socketHandler.store().name())) .issuer("smb:wa") .serial(Math.abs(ThreadLocalRandom.current().nextLong())) .build(); @@ -1110,6 +1186,7 @@ private CompletableFuture getBusinessCategoryNode() { private void onInitialInfo() { socketHandler.keys().setRegistered(true); schedulePing(); + retryConnection.set(false); socketHandler.onLoggedIn(); if (!socketHandler.keys().initialAppSync()) { return; @@ -1129,7 +1206,6 @@ private CompletableFuture queryRequiredMobileInfo() { return checkBusinessStatus() .thenCompose(ignored -> socketHandler.sendQuery("get", "w", Node.of("props", Map.of("protocol", "2", "hash", "")))) .thenAcceptAsync(this::parseProps) - .thenComposeAsync(ignored -> queryAbProps()) .thenRunAsync(() -> { socketHandler.sendQuery("get", "w:b", Node.of("lists")) .exceptionallyAsync(exception -> socketHandler.handleFailure(LOGIN, exception)); @@ -1146,21 +1222,15 @@ private CompletableFuture queryRequiredMobileInfo() { private CompletableFuture queryRequiredWebInfo() { return socketHandler.sendQuery("get", "w", Node.of("props")) .thenAcceptAsync(this::parseProps) - .thenComposeAsync(ignored -> queryAbProps()) .exceptionallyAsync(exception -> socketHandler.handleFailure(LOGIN, exception)); } - private CompletableFuture queryAbProps() { - return socketHandler.sendQuery("get", "abt", Node.of("props", Map.of("protocol", "1"))) - .thenAcceptAsync(result -> { /* TODO: Handle AB props */ }); - } - private CompletableFuture checkBusinessStatus() { if (!socketHandler.store().device().platform().isBusiness() || socketHandler.keys().businessCertificate()) { return CompletableFuture.completedFuture(null); } - return CompletableFuture.allOf(setBusinessCertificate(), setBusinessProfile()) + return CompletableFuture.allOf(updateBusinessCertificate(null), setBusinessProfile()) .thenRunAsync(() -> socketHandler.keys().setBusinessCertificate(true)); } @@ -1291,28 +1361,26 @@ private void schedulePing() { return; } - this.pingFuture = socketHandler.scheduleAtFixedInterval(() -> { + socketHandler.scheduleAtFixedInterval(() -> { socketHandler.sendPing(); socketHandler.store().serialize(true); socketHandler.store().serializer().linkMetadata(socketHandler.store()); socketHandler.keys().serialize(true); - }, PING_INTERVAL, PING_INTERVAL); + }, PING_INTERVAL / 2, PING_INTERVAL); } - private CompletableFuture scheduleMediaConnectionUpdate(int tries, Throwable error) { + private CompletableFuture scheduleMediaConnectionUpdate() { if (socketHandler.state() != SocketState.CONNECTED) { return CompletableFuture.completedFuture(null); } - if (tries >= MAX_ATTEMPTS) { - socketHandler.store().setMediaConnection(null); - socketHandler.handleFailure(MEDIA_CONNECTION, error); - return CompletableFuture.completedFuture(null); - } - return socketHandler.sendQuery("set", "w:m", Node.of("media_conn")) .thenAcceptAsync(this::onMediaConnection) - .exceptionallyCompose(throwable -> scheduleMediaConnectionUpdate(tries + 1, throwable)); + .exceptionallyAsync(throwable -> { + socketHandler.store().setMediaConnection(null); + socketHandler.handleFailure(MEDIA_CONNECTION, throwable); + return null; + }); } private void onMediaConnection(Node node) { @@ -1327,13 +1395,8 @@ private void onMediaConnection(Node node) { .map(attributes -> attributes.getString("hostname")) .toList(); var result = new MediaConnection(auth, ttl, maxBuckets, timestamp, hosts); - var alreadyScheduled = socketHandler.store().hasMediaConnection(); socketHandler.store().setMediaConnection(result); - if(alreadyScheduled) { - return; - } - - this.mediaConnectionFuture = socketHandler.scheduleAtFixedInterval(() -> scheduleMediaConnectionUpdate(0, null), result.ttl(), result.ttl()); + socketHandler.scheduleDelayed(this::scheduleMediaConnectionUpdate, result.ttl()); } private void digestIq(Node node) { @@ -1350,8 +1413,7 @@ private void digestIq(Node node) { private void sendPreKeys() { var startId = socketHandler.keys().lastPreKeyId() + 1; - var toUpload = socketHandler.store().clientType() == ClientType.MOBILE ? MOBILE_PRE_KEYS_UPLOAD_CHUNK : WEB_PRE_KEYS_UPLOAD_CHUNK; - var preKeys = IntStream.range(startId, startId + toUpload) + var preKeys = IntStream.range(startId, startId + WEB_PRE_KEYS_UPLOAD_CHUNK) .mapToObj(SignalPreKeyPair::random) .peek(socketHandler.keys()::addPreKey) .map(SignalPreKeyPair::toNode) @@ -1362,7 +1424,8 @@ private void sendPreKeys() { Node.of("registration", socketHandler.keys().encodedRegistrationId()), Node.of("type", KEY_BUNDLE_TYPE), Node.of("identity", socketHandler.keys().identityKeyPair().publicKey()), - Node.of("list", preKeys), socketHandler.keys().signedKeyPair().toNode() + Node.of("list", preKeys), + socketHandler.keys().signedKeyPair().toNode() ); } @@ -1452,6 +1515,7 @@ private void confirmPairing(Node node, Node container) { return; } var account = SignedDeviceIdentitySpec.decode(advIdentity.details()); + socketHandler.keys().setCompanionIdentity(account); var message = Bytes.concat( ACCOUNT_SIGNATURE_HEADER, account.details(), @@ -1484,9 +1548,25 @@ private void confirmPairing(Node node, Node container) { ) ); socketHandler.keys().companionIdentity(result); + var device = socketHandler.store().device(); + var platform = getWebPlatform(node); + socketHandler.store().setDevice(device.withPlatform(platform)); sendConfirmNode(node, devicePairNode); } + private UserAgent.PlatformType getWebPlatform(Node node) { + var name = node.findNode("platform") + .flatMap(entry -> entry.attributes().getOptionalString("name")) + .orElse(null); + return switch (name) { + case "smbi" -> UserAgent.PlatformType.IOS_BUSINESS; + case "smba" -> UserAgent.PlatformType.ANDROID_BUSINESS; + case "android" -> UserAgent.PlatformType.ANDROID; + case "ios" -> UserAgent.PlatformType.IOS; + case null, default -> null; + }; + } + private void sendConfirmNode(Node node, Node content) { var attributes = Attributes.of() .put("id", node.id()) @@ -1511,18 +1591,6 @@ private void saveCompanion(Node container) { } protected void dispose() { - if(mediaConnectionFuture != null && !mediaConnectionFuture.isDone()) { - mediaConnectionFuture.cancel(true); - } - - if(pingFuture != null) { - pingFuture.cancel(true); - } - - if(mediaConnectionFuture != null) { - mediaConnectionFuture.cancel(true); - } - retries.clear(); lastLinkCodeKey.set(null); } diff --git a/src/main/java/it/auties/whatsapp/binary/BinaryDecoder.java b/src/main/java/it/auties/whatsapp/io/BinaryDecoder.java similarity index 93% rename from src/main/java/it/auties/whatsapp/binary/BinaryDecoder.java rename to src/main/java/it/auties/whatsapp/io/BinaryDecoder.java index 08dbc9bd5..4a439ac81 100644 --- a/src/main/java/it/auties/whatsapp/binary/BinaryDecoder.java +++ b/src/main/java/it/auties/whatsapp/io/BinaryDecoder.java @@ -1,4 +1,4 @@ -package it.auties.whatsapp.binary; +package it.auties.whatsapp.io; import it.auties.whatsapp.model.jid.Jid; import it.auties.whatsapp.model.jid.JidServer; @@ -10,12 +10,9 @@ import java.io.DataInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; -import static it.auties.whatsapp.binary.BinaryTag.*; +import static it.auties.whatsapp.io.BinaryTag.*; public final class BinaryDecoder implements AutoCloseable { private final DataInputStream dataInputStream; @@ -151,11 +148,19 @@ private Map readAttributes(int size) throws IOException { for (var pair = size - 1; pair > 1; pair -= 2) { var key = readString(); var value = read(true); - map.put(key, value); + map.put(key, getValueWithContext(key, value)); } return map; } + private static Object getValueWithContext(String key, Object value) { + if (value instanceof Jid jid && Objects.equals(key, "lid")) { + return jid.withServer(JidServer.LID); + } + + return value; + } + @Override public void close() throws IOException { this.closed = true; diff --git a/src/main/java/it/auties/whatsapp/binary/BinaryEncoder.java b/src/main/java/it/auties/whatsapp/io/BinaryEncoder.java similarity index 99% rename from src/main/java/it/auties/whatsapp/binary/BinaryEncoder.java rename to src/main/java/it/auties/whatsapp/io/BinaryEncoder.java index 3f13759e6..a4c250e49 100644 --- a/src/main/java/it/auties/whatsapp/binary/BinaryEncoder.java +++ b/src/main/java/it/auties/whatsapp/io/BinaryEncoder.java @@ -1,4 +1,4 @@ -package it.auties.whatsapp.binary; +package it.auties.whatsapp.io; import it.auties.whatsapp.model.jid.Jid; import it.auties.whatsapp.model.node.Node; @@ -11,7 +11,7 @@ import java.util.List; import java.util.Objects; -import static it.auties.whatsapp.binary.BinaryTag.*; +import static it.auties.whatsapp.io.BinaryTag.*; public final class BinaryEncoder implements AutoCloseable { private static final int UNSIGNED_BYTE_MAX_VALUE = 256; diff --git a/src/main/java/it/auties/whatsapp/binary/BinaryTag.java b/src/main/java/it/auties/whatsapp/io/BinaryTag.java similarity index 95% rename from src/main/java/it/auties/whatsapp/binary/BinaryTag.java rename to src/main/java/it/auties/whatsapp/io/BinaryTag.java index 5aa3e2d61..f41937ef4 100644 --- a/src/main/java/it/auties/whatsapp/binary/BinaryTag.java +++ b/src/main/java/it/auties/whatsapp/io/BinaryTag.java @@ -1,4 +1,4 @@ -package it.auties.whatsapp.binary; +package it.auties.whatsapp.io; import java.util.Arrays; diff --git a/src/main/java/it/auties/whatsapp/binary/BinaryTokens.java b/src/main/java/it/auties/whatsapp/io/BinaryTokens.java similarity index 99% rename from src/main/java/it/auties/whatsapp/binary/BinaryTokens.java rename to src/main/java/it/auties/whatsapp/io/BinaryTokens.java index 0f20c47ed..52c45fb68 100644 --- a/src/main/java/it/auties/whatsapp/binary/BinaryTokens.java +++ b/src/main/java/it/auties/whatsapp/io/BinaryTokens.java @@ -1,4 +1,4 @@ -package it.auties.whatsapp.binary; +package it.auties.whatsapp.io; import it.auties.whatsapp.model.companion.CompanionProperty; diff --git a/src/main/java/it/auties/whatsapp/listener/Listener.java b/src/main/java/it/auties/whatsapp/listener/Listener.java index f4ff1dacc..cb3c83e6a 100644 --- a/src/main/java/it/auties/whatsapp/listener/Listener.java +++ b/src/main/java/it/auties/whatsapp/listener/Listener.java @@ -8,19 +8,18 @@ import it.auties.whatsapp.model.call.Call; import it.auties.whatsapp.model.chat.Chat; import it.auties.whatsapp.model.contact.Contact; -import it.auties.whatsapp.model.contact.ContactStatus; import it.auties.whatsapp.model.info.ChatMessageInfo; import it.auties.whatsapp.model.info.MessageIndexInfo; import it.auties.whatsapp.model.info.MessageInfo; import it.auties.whatsapp.model.info.QuotedMessageInfo; import it.auties.whatsapp.model.jid.Jid; +import it.auties.whatsapp.model.jid.JidProvider; import it.auties.whatsapp.model.mobile.CountryLocale; import it.auties.whatsapp.model.newsletter.Newsletter; import it.auties.whatsapp.model.node.Node; import it.auties.whatsapp.model.privacy.PrivacySettingEntry; import it.auties.whatsapp.model.setting.Setting; -import java.net.URI; import java.util.Collection; import java.util.List; import java.util.Map; @@ -71,8 +70,8 @@ default void onNodeReceived(Node incoming) { /** * Called when the socket successfully establishes a connection and logs in into an account. When * this event is called, any data, including chats and contact, is not guaranteed to be already in - * memory. Instead, {@link OnChats#onChats(Whatsapp, Collection)} ()} and - * {@link OnContacts#onContacts(Whatsapp, Collection)} ()} should be used. + * memory. Instead, {@link Listener#onChats(Whatsapp, Collection)} ()} and + * {@link Listener#onContacts(Whatsapp, Collection)} ()} should be used. * * @param whatsapp an instance to the calling api */ @@ -206,9 +205,8 @@ default void onContacts(Collection contacts) { * @param whatsapp an instance to the calling api * @param chat the chat that this update regards * @param jid the contact that this update regards - * @param status the new status of the contact */ - default void onContactPresence(Whatsapp whatsapp, Chat chat, Jid jid, ContactStatus status) { + default void onContactPresence(Whatsapp whatsapp, Chat chat, JidProvider jid) { } /** @@ -216,9 +214,8 @@ default void onContactPresence(Whatsapp whatsapp, Chat chat, Jid jid, ContactSta * * @param chat the chat that this update regards * @param jid the contact that this update regards - * @param status the new status of the contact */ - default void onContactPresence(Chat chat, Jid jid, ContactStatus status) { + default void onContactPresence(Chat chat, JidProvider jid) { } /** @@ -507,25 +504,6 @@ default void onAboutChanged(String oldAbout, String newAbout) { default void onAboutChanged(Whatsapp whatsapp, String oldAbout, String newAbout) { } - /** - * Called when the companion's picture changes - * - * @param oldPicture the non-null old picture - * @param newPicture the non-null new picture - */ - default void onProfilePictureChanged(URI oldPicture, URI newPicture) { - } - - /** - * Called when the companion's picture changes - * - * @param whatsapp an instance to the calling api - * @param oldPicture the non-null old picture - * @param newPicture the non-null new picture - */ - default void onProfilePictureChanged(Whatsapp whatsapp, URI oldPicture, URI newPicture) { - } - /** * Called when the companion's locale changes * diff --git a/src/main/java/it/auties/whatsapp/listener/ListenerConsumer.java b/src/main/java/it/auties/whatsapp/listener/ListenerConsumer.java new file mode 100644 index 000000000..21c4b4a6b --- /dev/null +++ b/src/main/java/it/auties/whatsapp/listener/ListenerConsumer.java @@ -0,0 +1,19 @@ +package it.auties.whatsapp.listener; + +public sealed interface ListenerConsumer { + non-sealed interface Empty extends ListenerConsumer { + void accept(); + } + + non-sealed interface Unary extends ListenerConsumer { + void accept(F value); + } + + non-sealed interface Binary extends ListenerConsumer { + void accept(F first, S second); + } + + non-sealed interface Ternary extends ListenerConsumer { + void accept(F first, S second, T third); + } +} diff --git a/src/main/java/it/auties/whatsapp/listener/OnAction.java b/src/main/java/it/auties/whatsapp/listener/OnAction.java deleted file mode 100644 index 675f3785b..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnAction.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.action.Action; -import it.auties.whatsapp.model.info.MessageIndexInfo; - -public interface OnAction extends Listener { - /** - * Called when the socket receives a sync from Whatsapp. - * - * @param action the sync that was executed - * @param messageIndexInfo the data about this action - */ - @Override - void onAction(Action action, MessageIndexInfo messageIndexInfo); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnCall.java b/src/main/java/it/auties/whatsapp/listener/OnCall.java deleted file mode 100644 index 0653e5865..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnCall.java +++ /dev/null @@ -1,13 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.call.Call; - -public interface OnCall extends Listener { - /** - * Called when a phone call arrives - * - * @param call the non-null phone call - */ - @Override - void onCall(Call call); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnChatMessagesSync.java b/src/main/java/it/auties/whatsapp/listener/OnChatMessagesSync.java deleted file mode 100644 index 6ac9d371c..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnChatMessagesSync.java +++ /dev/null @@ -1,14 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.chat.Chat; - -public interface OnChatMessagesSync extends Listener { - /** - * Called when the socket receives the recent message for a chat - * - * @param chat the chat - * @param last whether the messages in this chat are complete or there are more coming - */ - @Override - void onChatMessagesSync(Chat chat, boolean last); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnChats.java b/src/main/java/it/auties/whatsapp/listener/OnChats.java deleted file mode 100644 index 136e965a6..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnChats.java +++ /dev/null @@ -1,20 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.controller.Store; -import it.auties.whatsapp.model.chat.Chat; - -import java.util.Collection; - -public interface OnChats extends Listener { - /** - * Called when the socket receives all the chats from WhatsappWeb's WebSocket. When this event is - * fired, it is guaranteed that all metadata excluding messages will be present. To access this - * data use {@link Store#chats()}. 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 chats the chats - */ - @Override - void onChats(Collection chats); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnContactBlocked.java b/src/main/java/it/auties/whatsapp/listener/OnContactBlocked.java deleted file mode 100644 index 9ff2d27cb..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnContactBlocked.java +++ /dev/null @@ -1,13 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.contact.Contact; - -public interface OnContactBlocked extends Listener { - /** - * Called when a contact is blocked or unblocked - * - * @param contact the non-null contact - */ - @Override - void onContactBlocked(Contact contact); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnContactPictureChanged.java b/src/main/java/it/auties/whatsapp/listener/OnContactPictureChanged.java deleted file mode 100644 index f742918a5..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnContactPictureChanged.java +++ /dev/null @@ -1,13 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.contact.Contact; - -public interface OnContactPictureChanged extends Listener { - /** - * Called when a contact's profile picture changes - * - * @param contact the contact whose pic changed - */ - @Override - void onProfilePictureChanged(Contact contact); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnContactPresence.java b/src/main/java/it/auties/whatsapp/listener/OnContactPresence.java deleted file mode 100644 index 5f3ffbc6a..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnContactPresence.java +++ /dev/null @@ -1,17 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.chat.Chat; -import it.auties.whatsapp.model.contact.ContactStatus; -import it.auties.whatsapp.model.jid.Jid; - -public interface OnContactPresence extends Listener { - /** - * Called when the socket receives an update regarding the presence of a contact - * - * @param chat the chat that this update regards - * @param jid the contact that this update regards - * @param status the new status of the contact - */ - @Override - void onContactPresence(Chat chat, Jid jid, ContactStatus status); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnContacts.java b/src/main/java/it/auties/whatsapp/listener/OnContacts.java deleted file mode 100644 index e40c359d3..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnContacts.java +++ /dev/null @@ -1,16 +0,0 @@ -package it.auties.whatsapp.listener; - - -import it.auties.whatsapp.model.contact.Contact; - -import java.util.Collection; - -public interface OnContacts extends Listener { - /** - * Called when the socket receives all the contacts from WhatsappWeb's WebSocket - * - * @param contacts the contacts - */ - @Override - void onContacts(Collection contacts); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnDisconnected.java b/src/main/java/it/auties/whatsapp/listener/OnDisconnected.java deleted file mode 100644 index cf2010ca8..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnDisconnected.java +++ /dev/null @@ -1,14 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.DisconnectReason; - -public interface OnDisconnected extends Listener { - /** - * Called when the socket successfully disconnects from WhatsappWeb's WebSocket - * - * @param reason the errorReason why the session was disconnected - */ - @Override - void onDisconnected(DisconnectReason reason); -} - diff --git a/src/main/java/it/auties/whatsapp/listener/OnFeatures.java b/src/main/java/it/auties/whatsapp/listener/OnFeatures.java deleted file mode 100644 index 8d02ebb29..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnFeatures.java +++ /dev/null @@ -1,13 +0,0 @@ -package it.auties.whatsapp.listener; - -import java.util.List; - -public interface OnFeatures extends Listener { - /** - * Called when the socket receives new features from Whatsapp. - * - * @param features the non-null features that were sent - */ - @Override - void onFeatures(List features); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnGroupPictureChange.java b/src/main/java/it/auties/whatsapp/listener/OnGroupPictureChange.java deleted file mode 100644 index b12b15aff..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnGroupPictureChange.java +++ /dev/null @@ -1,13 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.chat.Chat; - -public interface OnGroupPictureChange extends Listener { - /** - * Called when a group's picture changes - * - * @param group the group whose pic changed - */ - @Override - void onGroupPictureChanged(Chat group); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnHistorySyncProgress.java b/src/main/java/it/auties/whatsapp/listener/OnHistorySyncProgress.java deleted file mode 100644 index 2b926c7ad..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnHistorySyncProgress.java +++ /dev/null @@ -1,12 +0,0 @@ -package it.auties.whatsapp.listener; - -public interface OnHistorySyncProgress extends Listener { - /** - * Called when the socket receives the sync percentage for the full or recent chunk of messages. - * This method is only called when the QR is first scanned and history is being synced. - * - * @param percentage the percentage synced up to now - * @param recent whether the sync is about the recent messages or older messages - */ - void onHistorySyncProgress(int percentage, boolean recent); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnLinkedDevices.java b/src/main/java/it/auties/whatsapp/listener/OnLinkedDevices.java deleted file mode 100644 index 94e8edf1e..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnLinkedDevices.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.jid.Jid; - -import java.util.Collection; - -public interface OnLinkedDevices extends Listener { - /** - * Called when the list of companion devices is updated - * - * @param devices the non-null devices - */ - @Override - void onLinkedDevices(Collection devices); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnLoggedIn.java b/src/main/java/it/auties/whatsapp/listener/OnLoggedIn.java deleted file mode 100644 index 76a40b17b..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnLoggedIn.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.auties.whatsapp.listener; - -import java.util.Collection; - -public interface OnLoggedIn extends Listener { - /** - * Called when the socket successfully establishes a connection and logs in into an account. When - * this event is called, any data, including chats and contact, is not guaranteed to be already in - * memory. Instead, {@link OnChats#onChats(Collection)} ()} and - * {@link OnContacts#onContacts(Collection)} ()} should be used. - */ - @Override - void onLoggedIn(); -} - diff --git a/src/main/java/it/auties/whatsapp/listener/OnMessageDeleted.java b/src/main/java/it/auties/whatsapp/listener/OnMessageDeleted.java deleted file mode 100644 index e8abf25f1..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnMessageDeleted.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.info.MessageInfo; - -public interface OnMessageDeleted extends Listener { - /** - * Called when a message is deleted - * - * @param info the message that was deleted - * @param everyone whether this message was deleted by you only for yourself or whether the - * message was permanently removed - */ - @Override - void onMessageDeleted(MessageInfo info, boolean everyone); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnMessageReply.java b/src/main/java/it/auties/whatsapp/listener/OnMessageReply.java deleted file mode 100644 index 062cfd22c..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnMessageReply.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.info.ChatMessageInfo; -import it.auties.whatsapp.model.info.QuotedMessageInfo; - -public interface OnMessageReply extends Listener { - /** - * Called when a message answers a previous message - * - * @param info the answer message - * @param quoted the quoted message - */ - @Override - void onMessageReply(ChatMessageInfo info, QuotedMessageInfo quoted); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnMessageStatus.java b/src/main/java/it/auties/whatsapp/listener/OnMessageStatus.java deleted file mode 100644 index 290b05d16..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnMessageStatus.java +++ /dev/null @@ -1,13 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.info.MessageInfo; - -public interface OnMessageStatus extends Listener { - /** - * Called when the status of a message changes - * - * @param info the message whose status changed - */ - @Override - void onMessageStatus(MessageInfo info); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnMetadata.java b/src/main/java/it/auties/whatsapp/listener/OnMetadata.java deleted file mode 100644 index 157958386..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnMetadata.java +++ /dev/null @@ -1,16 +0,0 @@ -package it.auties.whatsapp.listener; - -import java.util.Map; - -public interface OnMetadata extends Listener { - /** - * Called when an updated list of properties is received. This method is called both when a - * connection is established with WhatsappWeb and when new props are available. In the latter case - * though, this object should be considered as partial and is guaranteed to contain only updated - * entries. - * - * @param metadata the updated list of properties - */ - @Override - void onMetadata(Map metadata); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnNewContact.java b/src/main/java/it/auties/whatsapp/listener/OnNewContact.java deleted file mode 100644 index ba4adbf3c..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnNewContact.java +++ /dev/null @@ -1,14 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.contact.Contact; - -public interface OnNewContact extends Listener { - /** - * Called when the socket receives a new contact. There isn't an overloaded method with a Whatsapp - * parameter due to technical limitations. - * - * @param contact the new contact - */ - @Override - void onNewContact(Contact contact); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnNewMessage.java b/src/main/java/it/auties/whatsapp/listener/OnNewMessage.java deleted file mode 100644 index b0043f524..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnNewMessage.java +++ /dev/null @@ -1,13 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.info.MessageInfo; - -public interface OnNewMessage extends Listener { - /** - * Called when a new message is received in a chat - * - * @param info the message that was sent - */ - @Override - void onNewMessage(MessageInfo info); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnNewStatus.java b/src/main/java/it/auties/whatsapp/listener/OnNewStatus.java deleted file mode 100644 index d58fd8903..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnNewStatus.java +++ /dev/null @@ -1,13 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.info.ChatMessageInfo; - -public interface OnNewStatus extends Listener { - /** - * Called when the socket receives a new status from WhatsappWeb's Socket - * - * @param status the new status message - */ - @Override - void onNewStatus(ChatMessageInfo status); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnNewsletters.java b/src/main/java/it/auties/whatsapp/listener/OnNewsletters.java deleted file mode 100644 index b16332303..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnNewsletters.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.newsletter.Newsletter; - -import java.util.Collection; - -public interface OnNewsletters extends Listener { - /** - * Called when the socket receives all the newsletters from WhatsappWeb's Socket - * - * @param newsletters the newsletters - */ - @Override - void onNewsletters(Collection newsletters); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnNodeReceived.java b/src/main/java/it/auties/whatsapp/listener/OnNodeReceived.java deleted file mode 100644 index d952b0c80..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnNodeReceived.java +++ /dev/null @@ -1,13 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.node.Node; - -public interface OnNodeReceived extends Listener { - /** - * Called when the socket receives a node from Whatsapp - * - * @param incoming the non-null node that was just received - */ - @Override - void onNodeReceived(Node incoming); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnNodeSent.java b/src/main/java/it/auties/whatsapp/listener/OnNodeSent.java deleted file mode 100644 index 3c06930be..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnNodeSent.java +++ /dev/null @@ -1,13 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.node.Node; - -public interface OnNodeSent extends Listener { - /** - * Called when the socket sends a node to Whatsapp - * - * @param outgoing the non-null node that was just sent - */ - @Override - void onNodeSent(Node outgoing); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnPrivacySettingChanged.java b/src/main/java/it/auties/whatsapp/listener/OnPrivacySettingChanged.java deleted file mode 100644 index c1bceedd1..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnPrivacySettingChanged.java +++ /dev/null @@ -1,14 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.privacy.PrivacySettingEntry; - -public interface OnPrivacySettingChanged extends Listener { - /** - * Called when a privacy setting is modified - * - * @param oldPrivacyEntry the old entry - * @param newPrivacyEntry the new entry - */ - @Override - void onPrivacySettingChanged(PrivacySettingEntry oldPrivacyEntry, PrivacySettingEntry newPrivacyEntry); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnProfilePictureChanged.java b/src/main/java/it/auties/whatsapp/listener/OnProfilePictureChanged.java deleted file mode 100644 index 340fd767d..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnProfilePictureChanged.java +++ /dev/null @@ -1,14 +0,0 @@ -package it.auties.whatsapp.listener; - -import java.net.URI; - -public interface OnProfilePictureChanged extends Listener { - /** - * Called when the companion's picture changes - * - * @param oldPicture the non-null old picture - * @param newPicture the non-null new picture - */ - @Override - void onProfilePictureChanged(URI oldPicture, URI newPicture); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnRegistrationCode.java b/src/main/java/it/auties/whatsapp/listener/OnRegistrationCode.java deleted file mode 100644 index afab0c322..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnRegistrationCode.java +++ /dev/null @@ -1,12 +0,0 @@ -package it.auties.whatsapp.listener; - -public interface OnRegistrationCode extends Listener { - /** - * Called when an OTP is requested from a new device - * Only works on the mobile API - * - * @param code the registration code - */ - @Override - void onRegistrationCode(long code); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnSetting.java b/src/main/java/it/auties/whatsapp/listener/OnSetting.java deleted file mode 100644 index 66c831edf..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnSetting.java +++ /dev/null @@ -1,13 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.setting.Setting; - -public interface OnSetting extends Listener { - /** - * Called when the socket receives a setting change from Whatsapp. - * - * @param setting the setting that was toggled - */ - @Override - void onSetting(Setting setting); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnSocketEvent.java b/src/main/java/it/auties/whatsapp/listener/OnSocketEvent.java deleted file mode 100644 index 73949c705..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnSocketEvent.java +++ /dev/null @@ -1,13 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.SocketEvent; - -public interface OnSocketEvent extends Listener { - /** - * Called when an event regarding the underlying is fired - * - * @param event the event - */ - @Override - void onSocketEvent(SocketEvent event); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnStatus.java b/src/main/java/it/auties/whatsapp/listener/OnStatus.java deleted file mode 100644 index c18feabb2..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnStatus.java +++ /dev/null @@ -1,14 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.info.ChatMessageInfo; - -import java.util.Collection; - -public interface OnStatus extends Listener { - /** - * Called when the socket receives all the status updated from WhatsappWeb's Socket. - * - * @param status the status - */ - void onStatus(Collection status); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnUserAboutChanged.java b/src/main/java/it/auties/whatsapp/listener/OnUserAboutChanged.java deleted file mode 100644 index 509302bfc..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnUserAboutChanged.java +++ /dev/null @@ -1,11 +0,0 @@ -package it.auties.whatsapp.listener; - -public interface OnUserAboutChanged extends Listener { - /** - * Called when the companion's status changes - * - * @param oldAbout the non-null old about - * @param newAbout the non-null new about - */ - void onAboutChange(String oldAbout, String newAbout); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnUserLocaleChanged.java b/src/main/java/it/auties/whatsapp/listener/OnUserLocaleChanged.java deleted file mode 100644 index 8918af21b..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnUserLocaleChanged.java +++ /dev/null @@ -1,14 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.model.mobile.CountryLocale; - -public interface OnUserLocaleChanged extends Listener { - /** - * Called when the companion's locale changes - * - * @param oldLocale the non-null old locale - * @param newLocale the non-null new picture - */ - @Override - void onLocaleChanged(CountryLocale oldLocale, CountryLocale newLocale); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnUserNameChanged.java b/src/main/java/it/auties/whatsapp/listener/OnUserNameChanged.java deleted file mode 100644 index 72a1127bc..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnUserNameChanged.java +++ /dev/null @@ -1,12 +0,0 @@ -package it.auties.whatsapp.listener; - -public interface OnUserNameChanged extends Listener { - /** - * Called when the companion's name changes - * - * @param oldName the non-null old name - * @param newName the non-null new name - */ - @Override - void onNameChanged(String oldName, String newName); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappAboutChanged.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappAboutChanged.java deleted file mode 100644 index 8b3e136fc..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappAboutChanged.java +++ /dev/null @@ -1,14 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; - -public interface OnWhatsappAboutChanged extends Listener { - /** - * Called when the companion's status changes - * - * @param whatsapp an instance to the calling api - * @param oldAbout the non-null old about - * @param newAbout the non-null new about - */ - void onAboutChange(Whatsapp whatsapp, String oldAbout, String newAbout); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappAction.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappAction.java deleted file mode 100644 index eddeaf840..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappAction.java +++ /dev/null @@ -1,17 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.action.Action; -import it.auties.whatsapp.model.info.MessageIndexInfo; - -public interface OnWhatsappAction extends Listener { - /** - * Called when the socket receives a sync from Whatsapp. - * - * @param whatsapp an instance to the calling api - * @param action the sync that was executed - * @param messageIndexInfo the data about this action - */ - @Override - void onAction(Whatsapp whatsapp, Action action, MessageIndexInfo messageIndexInfo); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappCall.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappCall.java deleted file mode 100644 index 99c97df61..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappCall.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.call.Call; - -public interface OnWhatsappCall extends Listener { - /** - * Called when a phone call arrives - * - * @param whatsapp an instance to the calling api - * @param call the non-null phone call - */ - @Override - void onCall(Whatsapp whatsapp, Call call); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappChatMessagesSync.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappChatMessagesSync.java deleted file mode 100644 index faaaf6224..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappChatMessagesSync.java +++ /dev/null @@ -1,16 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.chat.Chat; - -public interface OnWhatsappChatMessagesSync extends Listener { - /** - * Called when the socket receives the recent message for a chat - * - * @param whatsapp an instance to the calling api - * @param chat the chat - * @param last whether the messages in this chat are complete or there are more coming - */ - @Override - void onChatMessagesSync(Whatsapp whatsapp, Chat chat, boolean last); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappChats.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappChats.java deleted file mode 100644 index 670afdc8c..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappChats.java +++ /dev/null @@ -1,21 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.chat.Chat; - -import java.util.Collection; - -public interface OnWhatsappChats extends Listener { - /** - * 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 chats the chats - */ - @Override - void onChats(Whatsapp whatsapp, Collection chats); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappContactBlocked.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappContactBlocked.java deleted file mode 100644 index 767822c06..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappContactBlocked.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.contact.Contact; - -public interface OnWhatsappContactBlocked extends Listener { - /** - * Called when a contact is blocked or unblocked - * - * @param whatsapp an instance to the calling api - * @param contact the non-null contact - */ - @Override - void onContactBlocked(Whatsapp whatsapp, Contact contact); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappContactPictureChanged.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappContactPictureChanged.java deleted file mode 100644 index 39d18f847..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappContactPictureChanged.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.contact.Contact; - -public interface OnWhatsappContactPictureChanged extends Listener { - /** - * Called when a contact's profile picture changes - * - * @param whatsapp an instance to the calling api - * @param contact the contact whose pic changed - */ - @Override - void onProfilePictureChanged(Whatsapp whatsapp, Contact contact); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappContactPresence.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappContactPresence.java deleted file mode 100644 index 26f69ec7b..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappContactPresence.java +++ /dev/null @@ -1,19 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.chat.Chat; -import it.auties.whatsapp.model.contact.ContactStatus; -import it.auties.whatsapp.model.jid.Jid; - -public interface OnWhatsappContactPresence extends Listener { - /** - * Called when the socket receives an update regarding the presence of a contact - * - * @param whatsapp an instance to the calling api - * @param chat the chat that this update regards - * @param jid the contact that this update regards - * @param status the new status of the contact - */ - @Override - void onContactPresence(Whatsapp whatsapp, Chat chat, Jid jid, ContactStatus status); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappContacts.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappContacts.java deleted file mode 100644 index a20b6507e..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappContacts.java +++ /dev/null @@ -1,18 +0,0 @@ -package it.auties.whatsapp.listener; - - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.contact.Contact; - -import java.util.Collection; - -public interface OnWhatsappContacts extends Listener { - /** - * Called when the socket receives all the contacts from WhatsappWeb's WebSocket - * - * @param whatsapp an instance to the calling api - * @param contacts the contacts - */ - @Override - void onContacts(Whatsapp whatsapp, Collection contacts); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappDisconnected.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappDisconnected.java deleted file mode 100644 index f17ad7cdd..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappDisconnected.java +++ /dev/null @@ -1,16 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.DisconnectReason; -import it.auties.whatsapp.api.Whatsapp; - -public interface OnWhatsappDisconnected extends Listener { - /** - * Called when the socket successfully disconnects from WhatsappWeb's WebSocket - * - * @param whatsapp an instance to the calling api - * @param reason the errorReason why the session was disconnected - */ - @Override - void onDisconnected(Whatsapp whatsapp, DisconnectReason reason); -} - diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappFeatures.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappFeatures.java deleted file mode 100644 index 735bfcaf7..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappFeatures.java +++ /dev/null @@ -1,16 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; - -import java.util.List; - -public interface OnWhatsappFeatures extends Listener { - /** - * Called when the socket receives new features from Whatsapp. - * - * @param whatsapp an instance to the calling api - * @param features the non-null features that were sent - */ - @Override - void onFeatures(Whatsapp whatsapp, List features); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappGroupPictureChange.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappGroupPictureChange.java deleted file mode 100644 index 0c7b7c945..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappGroupPictureChange.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.chat.Chat; - -public interface OnWhatsappGroupPictureChange extends Listener { - /** - * Called when a group's picture changes - * - * @param whatsapp an instance to the calling api - * @param group the group whose pic changed - */ - @Override - void onGroupPictureChanged(Whatsapp whatsapp, Chat group); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappHistorySyncProgress.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappHistorySyncProgress.java deleted file mode 100644 index 17740b4bf..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappHistorySyncProgress.java +++ /dev/null @@ -1,16 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; - -public interface OnWhatsappHistorySyncProgress extends Listener { - /** - * Called when the socket receives the sync percentage for the full or recent chunk of messages. - * This method is only called when the QR is first scanned and history is being synced. - * - * @param whatsapp an instance to the calling api - * @param percentage the percentage synced up to now - * @param recent whether the sync is about the recent messages or older messages - */ - @Override - void onHistorySyncProgress(Whatsapp whatsapp, int percentage, boolean recent); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappLinkedDevices.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappLinkedDevices.java deleted file mode 100644 index 6e11dcfc6..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappLinkedDevices.java +++ /dev/null @@ -1,17 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.jid.Jid; - -import java.util.Collection; - -public interface OnWhatsappLinkedDevices extends Listener { - /** - * Called when the list of companion devices is updated - * - * @param whatsapp an instance to the calling api - * @param devices the non-null devices - */ - @Override - void onLinkedDevices(Whatsapp whatsapp, Collection devices); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappLocaleChanged.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappLocaleChanged.java deleted file mode 100644 index 95dac22a6..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappLocaleChanged.java +++ /dev/null @@ -1,16 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.mobile.CountryLocale; - -public interface OnWhatsappLocaleChanged extends Listener { - /** - * Called when the companion's locale changes - * - * @param whatsapp an instance to the calling api - * @param oldLocale the non-null old locale - * @param newLocale the non-null new picture - */ - @Override - void onLocaleChanged(Whatsapp whatsapp, CountryLocale oldLocale, CountryLocale newLocale); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappLoggedIn.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappLoggedIn.java deleted file mode 100644 index 9500c0d1e..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappLoggedIn.java +++ /dev/null @@ -1,19 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; - -import java.util.Collection; - -public interface OnWhatsappLoggedIn extends Listener { - /** - * Called when the socket successfully establishes a connection and logs in into an account. When - * this event is called, any data, including chats and contact, is not guaranteed to be already in - * memory. Instead, {@link OnChats#onChats(Whatsapp, Collection)} ()} and - * {@link OnContacts#onContacts(Whatsapp, Collection)} ()} should be used. - * - * @param whatsapp an instance to the calling api - */ - @Override - void onLoggedIn(Whatsapp whatsapp); -} - diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappMediaStatus.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappMediaStatus.java deleted file mode 100644 index 208f1192f..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappMediaStatus.java +++ /dev/null @@ -1,16 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.info.ChatMessageInfo; - -import java.util.Collection; - -public interface OnWhatsappMediaStatus extends Listener { - /** - * Called when the socket receives all the status updated from WhatsappWeb's Socket. - * - * @param whatsapp an instance to the calling api - * @param status the status - */ - void onStatus(Whatsapp whatsapp, Collection status); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappMessageDeleted.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappMessageDeleted.java deleted file mode 100644 index cae83c614..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappMessageDeleted.java +++ /dev/null @@ -1,17 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.info.MessageInfo; - -public interface OnWhatsappMessageDeleted extends Listener { - /** - * Called when a message is deleted - * - * @param whatsapp an instance to the calling api - * @param info the message that was deleted - * @param everyone whether this message was deleted by you only for yourself or whether the - * message was permanently removed - */ - @Override - void onMessageDeleted(Whatsapp whatsapp, MessageInfo info, boolean everyone); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappMessageReply.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappMessageReply.java deleted file mode 100644 index b213b16cb..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappMessageReply.java +++ /dev/null @@ -1,17 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.info.ChatMessageInfo; -import it.auties.whatsapp.model.info.QuotedMessageInfo; - -public interface OnWhatsappMessageReply extends Listener { - /** - * Called when a message answers a previous message - * - * @param whatsapp an instance to the calling api - * @param info the answer message - * @param quoted the quoted message - */ - @Override - void onMessageReply(Whatsapp whatsapp, ChatMessageInfo info, QuotedMessageInfo quoted); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappMessageStatus.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappMessageStatus.java deleted file mode 100644 index 3f53759e8..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappMessageStatus.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.info.MessageInfo; - -public interface OnWhatsappMessageStatus extends Listener { - /** - * Called when the status of a message changes - * - * @param whatsapp an instance to the calling api - * @param info the message whose status changed - */ - @Override - void onMessageStatus(Whatsapp whatsapp, MessageInfo info); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappMetadata.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappMetadata.java deleted file mode 100644 index 5d29a342e..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappMetadata.java +++ /dev/null @@ -1,19 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; - -import java.util.Map; - -public interface OnWhatsappMetadata extends Listener { - /** - * Called when an updated list of properties is received. This method is called both when a - * connection is established with WhatsappWeb and when new props are available. In the latter case - * though, this object should be considered as partial and is guaranteed to contain only updated - * entries. - * - * @param whatsapp an instance to the calling api - * @param metadata the updated list of properties - */ - @Override - void onMetadata(Whatsapp whatsapp, Map metadata); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappNameChanged.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappNameChanged.java deleted file mode 100644 index aebb7060a..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappNameChanged.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; - -public interface OnWhatsappNameChanged extends Listener { - /** - * Called when the companion's name changes - * - * @param whatsapp an instance to the calling api - * @param oldName the non-null old name - * @param newName the non-null new name - */ - @Override - void onNameChanged(Whatsapp whatsapp, String oldName, String newName); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappNewMessage.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappNewMessage.java deleted file mode 100644 index 7ef360cc2..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappNewMessage.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.info.MessageInfo; - -public interface OnWhatsappNewMessage extends Listener { - /** - * Called when a new message is received in a chat - * - * @param whatsapp an instance to the calling api - * @param info the message that was sent - */ - @Override - void onNewMessage(Whatsapp whatsapp, MessageInfo info); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappNewStatus.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappNewStatus.java deleted file mode 100644 index a4136044f..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappNewStatus.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.info.ChatMessageInfo; - -public interface OnWhatsappNewStatus extends Listener { - /** - * Called when the socket receives a new status from WhatsappWeb's Socket - * - * @param whatsapp an instance to the calling api - * @param status the new status message - */ - @Override - void onNewStatus(Whatsapp whatsapp, ChatMessageInfo status); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappNewsletters.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappNewsletters.java deleted file mode 100644 index e53e1bb60..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappNewsletters.java +++ /dev/null @@ -1,17 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.newsletter.Newsletter; - -import java.util.Collection; - -public interface OnWhatsappNewsletters extends Listener { - /** - * Called when the socket receives all the newsletters from WhatsappWeb's Socket - * - * @param whatsapp an instance to the calling api - * @param newsletters the newsletters - */ - @Override - void onNewsletters(Whatsapp whatsapp, Collection newsletters); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappNodeReceived.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappNodeReceived.java deleted file mode 100644 index b38a1f186..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappNodeReceived.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.node.Node; - -public interface OnWhatsappNodeReceived extends Listener { - /** - * Called when the socket receives a node from Whatsapp - * - * @param whatsapp an instance to the calling api - * @param incoming the non-null node that was just received - */ - @Override - void onNodeReceived(Whatsapp whatsapp, Node incoming); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappNodeSent.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappNodeSent.java deleted file mode 100644 index ca9470919..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappNodeSent.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.node.Node; - -public interface OnWhatsappNodeSent extends Listener { - /** - * Called when the socket sends a node to Whatsapp - * - * @param whatsapp an instance to the calling api - * @param outgoing the non-null node that was just sent - */ - @Override - void onNodeSent(Whatsapp whatsapp, Node outgoing); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappPrivacySettingChanged.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappPrivacySettingChanged.java deleted file mode 100644 index d8ccdffce..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappPrivacySettingChanged.java +++ /dev/null @@ -1,16 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.privacy.PrivacySettingEntry; - -public interface OnWhatsappPrivacySettingChanged extends Listener { - /** - * Called when a privacy setting is modified - * - * @param whatsapp an instance to the calling api - * @param oldPrivacyEntry the old entry - * @param newPrivacyEntry the new entry - */ - @Override - void onPrivacySettingChanged(Whatsapp whatsapp, PrivacySettingEntry oldPrivacyEntry, PrivacySettingEntry newPrivacyEntry); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappProfilePictureChanged.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappProfilePictureChanged.java deleted file mode 100644 index 1c36ab149..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappProfilePictureChanged.java +++ /dev/null @@ -1,17 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; - -import java.net.URI; - -public interface OnWhatsappProfilePictureChanged extends Listener { - /** - * Called when the companion's picture changes - * - * @param whatsapp an instance to the calling api - * @param oldPicture the non-null old picture - * @param newPicture the non-null new picture - */ - @Override - void onProfilePictureChanged(Whatsapp whatsapp, URI oldPicture, URI newPicture); -} diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappRegistrationCode.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappRegistrationCode.java deleted file mode 100644 index 972929921..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappRegistrationCode.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; - -public interface OnWhatsappRegistrationCode extends Listener { - /** - * Called when an OTP is requested from a new device - * Only works on the mobile API - * - * @param whatsapp an instance to the calling api - * @param code the registration code - */ - @Override - void onRegistrationCode(Whatsapp whatsapp, long code); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappSetting.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappSetting.java deleted file mode 100644 index 8263c1f0d..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappSetting.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.model.setting.Setting; - -public interface OnWhatsappSetting extends Listener { - /** - * Called when the socket receives a setting change from Whatsapp. - * - * @param whatsapp an instance to the calling api - * @param setting the setting that was toggled - */ - @Override - void onSetting(Whatsapp whatsapp, Setting setting); -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/listener/OnWhatsappSocketEvent.java b/src/main/java/it/auties/whatsapp/listener/OnWhatsappSocketEvent.java deleted file mode 100644 index 2ae6323c8..000000000 --- a/src/main/java/it/auties/whatsapp/listener/OnWhatsappSocketEvent.java +++ /dev/null @@ -1,15 +0,0 @@ -package it.auties.whatsapp.listener; - -import it.auties.whatsapp.api.SocketEvent; -import it.auties.whatsapp.api.Whatsapp; - -public interface OnWhatsappSocketEvent extends Listener { - /** - * Called when an event regarding the underlying is fired - * - * @param whatsapp an instance to the calling api - * @param event the event - */ - @Override - void onSocketEvent(Whatsapp whatsapp, SocketEvent event); -} diff --git a/src/main/java/it/auties/whatsapp/listener/processor/RegisterListenerProcessor.java b/src/main/java/it/auties/whatsapp/listener/RegisterListenerProcessor.java similarity index 97% rename from src/main/java/it/auties/whatsapp/listener/processor/RegisterListenerProcessor.java rename to src/main/java/it/auties/whatsapp/listener/RegisterListenerProcessor.java index b52945cee..080f64954 100644 --- a/src/main/java/it/auties/whatsapp/listener/processor/RegisterListenerProcessor.java +++ b/src/main/java/it/auties/whatsapp/listener/RegisterListenerProcessor.java @@ -1,9 +1,7 @@ -package it.auties.whatsapp.listener.processor; +package it.auties.whatsapp.listener; import com.sun.source.util.TaskListener; import it.auties.whatsapp.api.Whatsapp; -import it.auties.whatsapp.listener.Listener; -import it.auties.whatsapp.listener.RegisterListener; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; diff --git a/src/main/java/it/auties/whatsapp/model/action/Action.java b/src/main/java/it/auties/whatsapp/model/action/Action.java index 87612c6ca..a77839db5 100644 --- a/src/main/java/it/auties/whatsapp/model/action/Action.java +++ b/src/main/java/it/auties/whatsapp/model/action/Action.java @@ -1,12 +1,11 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.whatsapp.model.sync.PatchType; /** * A model interface that represents an action */ -public sealed interface Action extends ProtobufMessage permits AgentAction, AndroidUnsupportedActions, ArchiveChatAction, ChatAssignmentAction, ChatAssignmentOpenedStatusAction, ClearChatAction, ContactAction, DeleteChatAction, DeleteMessageForMeAction, LabelAssociationAction, LabelEditAction, MarkChatAsReadAction, MuteAction, NuxAction, PinAction, PrimaryVersionAction, QuickReplyAction, RecentEmojiWeightsAction, RemoveRecentStickerAction, StarAction, StickerAction, SubscriptionAction, TimeFormatAction, UserStatusMuteAction { +public sealed interface Action permits AgentAction, AndroidUnsupportedActions, ArchiveChatAction, ChatAssignmentAction, ChatAssignmentOpenedStatusAction, ClearChatAction, ContactAction, DeleteChatAction, DeleteMessageForMeAction, LabelAssociationAction, LabelEditAction, MarkChatAsReadAction, MuteAction, NuxAction, PinAction, PrimaryVersionAction, QuickReplyAction, RecentEmojiWeightsAction, RemoveRecentStickerAction, StarAction, StickerAction, SubscriptionAction, TimeFormatAction, UserStatusMuteAction { /** * The name of this action * diff --git a/src/main/java/it/auties/whatsapp/model/action/AgentAction.java b/src/main/java/it/auties/whatsapp/model/action/AgentAction.java index da7ccb37d..176c74d72 100644 --- a/src/main/java/it/auties/whatsapp/model/action/AgentAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/AgentAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.PatchType; @@ -10,7 +10,7 @@ /** * A model clas that represents an agent */ -@ProtobufMessageName("SyncActionValue.AgentAction") +@ProtobufMessage(name = "SyncActionValue.AgentAction") public record AgentAction( @ProtobufProperty(index = 1, type = ProtobufType.STRING) Optional name, diff --git a/src/main/java/it/auties/whatsapp/model/action/AndroidUnsupportedActions.java b/src/main/java/it/auties/whatsapp/model/action/AndroidUnsupportedActions.java index 0084f7a3b..965c0e367 100644 --- a/src/main/java/it/auties/whatsapp/model/action/AndroidUnsupportedActions.java +++ b/src/main/java/it/auties/whatsapp/model/action/AndroidUnsupportedActions.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.PatchType; @@ -8,7 +8,7 @@ /** * A model clas that represents unsupported actions for android */ -@ProtobufMessageName("SyncActionValue.AndroidUnsupportedActions") +@ProtobufMessage(name = "SyncActionValue.AndroidUnsupportedActions") public record AndroidUnsupportedActions( @ProtobufProperty(index = 1, type = ProtobufType.BOOL) boolean allowed diff --git a/src/main/java/it/auties/whatsapp/model/action/ArchiveChatAction.java b/src/main/java/it/auties/whatsapp/model/action/ArchiveChatAction.java index e71a899a9..ad1282eb0 100644 --- a/src/main/java/it/auties/whatsapp/model/action/ArchiveChatAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/ArchiveChatAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.ActionMessageRangeSync; @@ -11,7 +11,7 @@ /** * A model clas that represents an archived chat */ -@ProtobufMessageName("SyncActionValue.ArchiveChatAction") +@ProtobufMessage(name = "SyncActionValue.ArchiveChatAction") public record ArchiveChatAction( @ProtobufProperty(index = 1, type = ProtobufType.BOOL) boolean archived, diff --git a/src/main/java/it/auties/whatsapp/model/action/ChatAssignmentAction.java b/src/main/java/it/auties/whatsapp/model/action/ChatAssignmentAction.java index 086c43647..1deec8e7d 100644 --- a/src/main/java/it/auties/whatsapp/model/action/ChatAssignmentAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/ChatAssignmentAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.PatchType; @@ -10,7 +10,7 @@ /** * A model clas that represents the assignment of a chat */ -@ProtobufMessageName("SyncActionValue.ChatAssignmentAction") +@ProtobufMessage(name = "SyncActionValue.ChatAssignmentAction") public record ChatAssignmentAction( @ProtobufProperty(index = 1, type = ProtobufType.STRING) Optional deviceAgentId diff --git a/src/main/java/it/auties/whatsapp/model/action/ChatAssignmentOpenedStatusAction.java b/src/main/java/it/auties/whatsapp/model/action/ChatAssignmentOpenedStatusAction.java index 602d5365b..358aa0284 100644 --- a/src/main/java/it/auties/whatsapp/model/action/ChatAssignmentOpenedStatusAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/ChatAssignmentOpenedStatusAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.PatchType; @@ -8,7 +8,7 @@ /** * A model clas that represents the assignment of a chat as opened */ -@ProtobufMessageName("SyncActionValue.ChatAssignmentOpenedStatusAction") +@ProtobufMessage(name = "SyncActionValue.ChatAssignmentOpenedStatusAction") public record ChatAssignmentOpenedStatusAction( @ProtobufProperty(index = 1, type = ProtobufType.BOOL) boolean chatOpened diff --git a/src/main/java/it/auties/whatsapp/model/action/ClearChatAction.java b/src/main/java/it/auties/whatsapp/model/action/ClearChatAction.java index cb099d23a..217356e9f 100644 --- a/src/main/java/it/auties/whatsapp/model/action/ClearChatAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/ClearChatAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.ActionMessageRangeSync; @@ -11,7 +11,7 @@ /** * A model clas that represents a cleared chat */ -@ProtobufMessageName("SyncActionValue.ClearChatAction") +@ProtobufMessage(name = "SyncActionValue.ClearChatAction") public record ClearChatAction( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) Optional messageRange diff --git a/src/main/java/it/auties/whatsapp/model/action/ContactAction.java b/src/main/java/it/auties/whatsapp/model/action/ContactAction.java index ee98adcc3..2570f1d5a 100644 --- a/src/main/java/it/auties/whatsapp/model/action/ContactAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/ContactAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.PatchType; @@ -10,7 +10,7 @@ /** * A model clas that represents a new contact push name */ -@ProtobufMessageName("SyncActionValue.ContactAction") +@ProtobufMessage(name = "SyncActionValue.ContactAction") public record ContactAction( @ProtobufProperty(index = 1, type = ProtobufType.STRING) Optional fullName, diff --git a/src/main/java/it/auties/whatsapp/model/action/DeleteChatAction.java b/src/main/java/it/auties/whatsapp/model/action/DeleteChatAction.java index b41fa6920..1cb838087 100644 --- a/src/main/java/it/auties/whatsapp/model/action/DeleteChatAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/DeleteChatAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.ActionMessageRangeSync; @@ -11,7 +11,7 @@ /** * A model clas that represents a deleted chat */ -@ProtobufMessageName("SyncActionValue.DeleteChatAction") +@ProtobufMessage(name = "SyncActionValue.DeleteChatAction") public record DeleteChatAction( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) Optional messageRange diff --git a/src/main/java/it/auties/whatsapp/model/action/DeleteMessageForMeAction.java b/src/main/java/it/auties/whatsapp/model/action/DeleteMessageForMeAction.java index deead7313..8c11c579f 100644 --- a/src/main/java/it/auties/whatsapp/model/action/DeleteMessageForMeAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/DeleteMessageForMeAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.PatchType; @@ -12,7 +12,7 @@ /** * A model clas that represents a message deleted for this client */ -@ProtobufMessageName("SyncActionValue.DeleteMessageForMeAction") +@ProtobufMessage(name = "SyncActionValue.DeleteMessageForMeAction") public record DeleteMessageForMeAction( @ProtobufProperty(index = 1, type = ProtobufType.BOOL) boolean deleteMedia, diff --git a/src/main/java/it/auties/whatsapp/model/action/LabelAssociationAction.java b/src/main/java/it/auties/whatsapp/model/action/LabelAssociationAction.java index 743e343dc..9e98cbd05 100644 --- a/src/main/java/it/auties/whatsapp/model/action/LabelAssociationAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/LabelAssociationAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.PatchType; @@ -8,7 +8,7 @@ /** * A model clas that represents a label association */ -@ProtobufMessageName("SyncActionValue.LabelAssociationAction") +@ProtobufMessage(name = "SyncActionValue.LabelAssociationAction") public record LabelAssociationAction( @ProtobufProperty(index = 1, type = ProtobufType.BOOL) boolean labeled diff --git a/src/main/java/it/auties/whatsapp/model/action/LabelEditAction.java b/src/main/java/it/auties/whatsapp/model/action/LabelEditAction.java index 4fdefab46..41c97816e 100644 --- a/src/main/java/it/auties/whatsapp/model/action/LabelEditAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/LabelEditAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.PatchType; @@ -8,7 +8,7 @@ /** * A model clas that represents an edit to a label */ -@ProtobufMessageName("SyncActionValue.LabelEditAction") +@ProtobufMessage(name = "SyncActionValue.LabelEditAction") public record LabelEditAction( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String name, diff --git a/src/main/java/it/auties/whatsapp/model/action/MarkChatAsReadAction.java b/src/main/java/it/auties/whatsapp/model/action/MarkChatAsReadAction.java index ff13d80cc..577c4389f 100644 --- a/src/main/java/it/auties/whatsapp/model/action/MarkChatAsReadAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/MarkChatAsReadAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.ActionMessageRangeSync; @@ -11,7 +11,7 @@ /** * A model clas that represents a new read status for a chat */ -@ProtobufMessageName("SyncActionValue.MarkChatAsReadAction") +@ProtobufMessage(name = "SyncActionValue.MarkChatAsReadAction") public record MarkChatAsReadAction( @ProtobufProperty(index = 1, type = ProtobufType.BOOL) boolean read, diff --git a/src/main/java/it/auties/whatsapp/model/action/MuteAction.java b/src/main/java/it/auties/whatsapp/model/action/MuteAction.java index 506365628..6cf6ecc12 100644 --- a/src/main/java/it/auties/whatsapp/model/action/MuteAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/MuteAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.PatchType; @@ -13,7 +13,7 @@ /** * A model clas that represents a new mute status for a chat */ -@ProtobufMessageName("SyncActionValue.MuteAction") +@ProtobufMessage(name = "SyncActionValue.MuteAction") public record MuteAction( @ProtobufProperty(index = 1, type = ProtobufType.BOOL) boolean muted, diff --git a/src/main/java/it/auties/whatsapp/model/action/NuxAction.java b/src/main/java/it/auties/whatsapp/model/action/NuxAction.java index d3297d57e..32efac0fe 100644 --- a/src/main/java/it/auties/whatsapp/model/action/NuxAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/NuxAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.PatchType; @@ -8,7 +8,7 @@ /** * Unknown */ -@ProtobufMessageName("SyncActionValue.NuxAction") +@ProtobufMessage(name = "SyncActionValue.NuxAction") public record NuxAction( @ProtobufProperty(index = 1, type = ProtobufType.BOOL) boolean acknowledged diff --git a/src/main/java/it/auties/whatsapp/model/action/PinAction.java b/src/main/java/it/auties/whatsapp/model/action/PinAction.java index 7c8193565..2d41806ec 100644 --- a/src/main/java/it/auties/whatsapp/model/action/PinAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/PinAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.PatchType; @@ -8,7 +8,7 @@ /** * A model clas that represents a new pin status for a chat */ -@ProtobufMessageName("SyncActionValue.PinAction") +@ProtobufMessage(name = "SyncActionValue.PinAction") public record PinAction( @ProtobufProperty(index = 1, type = ProtobufType.BOOL) boolean pinned diff --git a/src/main/java/it/auties/whatsapp/model/action/PrimaryVersionAction.java b/src/main/java/it/auties/whatsapp/model/action/PrimaryVersionAction.java index bb7e1689c..efbc8951c 100644 --- a/src/main/java/it/auties/whatsapp/model/action/PrimaryVersionAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/PrimaryVersionAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.PatchType; @@ -8,7 +8,7 @@ /** * A model class that contains the main Whatsapp version being used */ -@ProtobufMessageName("SyncActionValue.PrimaryVersionAction") +@ProtobufMessage(name = "SyncActionValue.PrimaryVersionAction") public record PrimaryVersionAction( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String version 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 c69d53310..cf337f0dd 100644 --- a/src/main/java/it/auties/whatsapp/model/action/QuickReplyAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/QuickReplyAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.PatchType; @@ -10,7 +10,7 @@ /** * A model clas that represents the addition or deletion of a quick reply */ -@ProtobufMessageName("SyncActionValue.QuickReplyAction") +@ProtobufMessage(name = "SyncActionValue.QuickReplyAction") public record QuickReplyAction( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String shortcut, 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 918ff52f1..9e5564820 100644 --- a/src/main/java/it/auties/whatsapp/model/action/RecentEmojiWeightsAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/RecentEmojiWeightsAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.PatchType; @@ -11,7 +11,7 @@ /** * A model clas that represents a change in the weight of recent emojis */ -@ProtobufMessageName("SyncActionValue.RecentEmojiWeightsAction") +@ProtobufMessage(name = "SyncActionValue.RecentEmojiWeightsAction") public record RecentEmojiWeightsAction( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) List weights diff --git a/src/main/java/it/auties/whatsapp/model/action/RemoveRecentStickerAction.java b/src/main/java/it/auties/whatsapp/model/action/RemoveRecentStickerAction.java index 8dd204ef8..ac184ba03 100644 --- a/src/main/java/it/auties/whatsapp/model/action/RemoveRecentStickerAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/RemoveRecentStickerAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.PatchType; @@ -12,7 +12,7 @@ /** * A model class that represents the deletion of a sticker from the recent list */ -@ProtobufMessageName("SyncActionValue.RemoveRecentStickerAction") +@ProtobufMessage(name = "SyncActionValue.RemoveRecentStickerAction") public record RemoveRecentStickerAction( @ProtobufProperty(index = 1, type = ProtobufType.INT64) long lastStickerSentTimestampSeconds diff --git a/src/main/java/it/auties/whatsapp/model/action/StarAction.java b/src/main/java/it/auties/whatsapp/model/action/StarAction.java index 99a857991..5c64384ce 100644 --- a/src/main/java/it/auties/whatsapp/model/action/StarAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/StarAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.PatchType; @@ -8,7 +8,7 @@ /** * A model clas that represents a new star status for a message */ -@ProtobufMessageName("SyncActionValue.StarAction") +@ProtobufMessage(name = "SyncActionValue.StarAction") public record StarAction( @ProtobufProperty(index = 1, type = ProtobufType.BOOL) boolean starred diff --git a/src/main/java/it/auties/whatsapp/model/action/StickerAction.java b/src/main/java/it/auties/whatsapp/model/action/StickerAction.java index fe029beca..7d0b7c694 100644 --- a/src/main/java/it/auties/whatsapp/model/action/StickerAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/StickerAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.PatchType; @@ -9,7 +9,7 @@ /** * A model clas that represents a sticker */ -@ProtobufMessageName("SyncActionValue.StickerAction") +@ProtobufMessage(name = "SyncActionValue.StickerAction") public record StickerAction( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String url, diff --git a/src/main/java/it/auties/whatsapp/model/action/SubscriptionAction.java b/src/main/java/it/auties/whatsapp/model/action/SubscriptionAction.java index 5b05375c9..aabbbe8b5 100644 --- a/src/main/java/it/auties/whatsapp/model/action/SubscriptionAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/SubscriptionAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.PatchType; @@ -12,7 +12,7 @@ /** * A model clas that represents a subscription */ -@ProtobufMessageName("SyncActionValue.SubscriptionAction") +@ProtobufMessage(name = "SyncActionValue.SubscriptionAction") public record SubscriptionAction( @ProtobufProperty(index = 1, type = ProtobufType.BOOL) boolean deactivated, diff --git a/src/main/java/it/auties/whatsapp/model/action/TimeFormatAction.java b/src/main/java/it/auties/whatsapp/model/action/TimeFormatAction.java index 7d0def9c7..f4f9f127c 100644 --- a/src/main/java/it/auties/whatsapp/model/action/TimeFormatAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/TimeFormatAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.PatchType; @@ -8,7 +8,7 @@ /** * A model clas that represents the time format used by the companion */ -@ProtobufMessageName("SyncActionValue.TimeFormatAction") +@ProtobufMessage(name = "SyncActionValue.TimeFormatAction") public record TimeFormatAction( @ProtobufProperty(index = 1, type = ProtobufType.BOOL) boolean twentyFourHourFormatEnabled diff --git a/src/main/java/it/auties/whatsapp/model/action/UserStatusMuteAction.java b/src/main/java/it/auties/whatsapp/model/action/UserStatusMuteAction.java index 8e0a47271..852541bed 100644 --- a/src/main/java/it/auties/whatsapp/model/action/UserStatusMuteAction.java +++ b/src/main/java/it/auties/whatsapp/model/action/UserStatusMuteAction.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.action; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.PatchType; @@ -8,7 +8,7 @@ /** * A model clas that represents whether a user was muted */ -@ProtobufMessageName("SyncActionValue.UserStatusMuteAction") +@ProtobufMessage(name = "SyncActionValue.UserStatusMuteAction") public record UserStatusMuteAction( @ProtobufProperty(index = 1, type = ProtobufType.BOOL) boolean muted diff --git a/src/main/java/it/auties/whatsapp/model/business/BusinessAccountPayload.java b/src/main/java/it/auties/whatsapp/model/business/BusinessAccountPayload.java index bd3e59411..f52c031a2 100644 --- a/src/main/java/it/auties/whatsapp/model/business/BusinessAccountPayload.java +++ b/src/main/java/it/auties/whatsapp/model/business/BusinessAccountPayload.java @@ -1,19 +1,18 @@ package it.auties.whatsapp.model.business; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; /** * A model class that holds a payload about a business account. */ -@ProtobufMessageName("BizAccountPayload") +@ProtobufMessage(name = "BizAccountPayload") public record BusinessAccountPayload( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) BusinessVerifiedNameCertificate certificate, @ProtobufProperty(index = 2, type = ProtobufType.BYTES) byte[] info -) implements ProtobufMessage { +) { } \ No newline at end of file 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 7b46ae062..c746779e3 100644 --- a/src/main/java/it/auties/whatsapp/model/business/BusinessCategory.java +++ b/src/main/java/it/auties/whatsapp/model/business/BusinessCategory.java @@ -1,7 +1,7 @@ package it.auties.whatsapp.model.business; +import it.auties.protobuf.annotation.ProtobufMessage; 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; @@ -14,12 +14,13 @@ * @param id the non-null id * @param name the non-null display name */ +@ProtobufMessage 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/BusinessLocalizedName.java b/src/main/java/it/auties/whatsapp/model/business/BusinessLocalizedName.java index 3d4b068c5..a3ace6d02 100644 --- a/src/main/java/it/auties/whatsapp/model/business/BusinessLocalizedName.java +++ b/src/main/java/it/auties/whatsapp/model/business/BusinessLocalizedName.java @@ -1,12 +1,13 @@ package it.auties.whatsapp.model.business; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; /** * A model class that represents a time a localizable name */ +@ProtobufMessage(name = "LocalizedName") public record BusinessLocalizedName( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String lg, @@ -14,5 +15,5 @@ public record BusinessLocalizedName( String lc, @ProtobufProperty(index = 3, type = ProtobufType.STRING) String name -) implements ProtobufMessage { +) { } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/business/BusinessPrivacyStatus.java b/src/main/java/it/auties/whatsapp/model/business/BusinessPrivacyStatus.java index 5aa07be3a..5a8b3a503 100644 --- a/src/main/java/it/auties/whatsapp/model/business/BusinessPrivacyStatus.java +++ b/src/main/java/it/auties/whatsapp/model/business/BusinessPrivacyStatus.java @@ -1,12 +1,13 @@ package it.auties.whatsapp.model.business; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; /** * The constants of this enumerated type describe the various types of business privacy */ -public enum BusinessPrivacyStatus implements ProtobufEnum { +@ProtobufEnum(name = "WebMessageInfo.BizPrivacyStatus") +public enum BusinessPrivacyStatus { /** * End-to-end encryption */ diff --git a/src/main/java/it/auties/whatsapp/model/business/BusinessVerifiedNameCertificate.java b/src/main/java/it/auties/whatsapp/model/business/BusinessVerifiedNameCertificate.java index 8343e251e..5e01fb9d6 100644 --- a/src/main/java/it/auties/whatsapp/model/business/BusinessVerifiedNameCertificate.java +++ b/src/main/java/it/auties/whatsapp/model/business/BusinessVerifiedNameCertificate.java @@ -1,14 +1,13 @@ package it.auties.whatsapp.model.business; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; /** * A model class that represents a business certificate */ -@ProtobufMessageName("VerifiedNameCertificate") +@ProtobufMessage(name = "VerifiedNameCertificate") public record BusinessVerifiedNameCertificate( @ProtobufProperty(index = 1, type = ProtobufType.BYTES) byte[] encodedDetails, @@ -16,7 +15,7 @@ public record BusinessVerifiedNameCertificate( byte[] signature, @ProtobufProperty(index = 3, type = ProtobufType.BYTES) byte[] serverSignature -) implements ProtobufMessage { +) { public BusinessVerifiedNameDetails details() { return BusinessVerifiedNameDetailsSpec.decode(encodedDetails); } 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 5720447c4..08a3123bf 100644 --- a/src/main/java/it/auties/whatsapp/model/business/BusinessVerifiedNameDetails.java +++ b/src/main/java/it/auties/whatsapp/model/business/BusinessVerifiedNameDetails.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.business; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.util.Clock; @@ -14,7 +13,7 @@ /** * A model class that represents a verified name */ -@ProtobufMessageName("VerifiedNameCertificate.Details") +@ProtobufMessage(name = "VerifiedNameCertificate.Details") public record BusinessVerifiedNameDetails( @ProtobufProperty(index = 1, type = ProtobufType.UINT64) long serial, @@ -26,7 +25,7 @@ public record BusinessVerifiedNameDetails( List localizedNames, @ProtobufProperty(index = 10, type = ProtobufType.UINT64) long issueTimeSeconds -) implements ProtobufMessage { +) { /** * Returns this object's timestampSeconds * diff --git a/src/main/java/it/auties/whatsapp/model/button/base/Button.java b/src/main/java/it/auties/whatsapp/model/button/base/Button.java index 8f2a4ca02..90cc3f50f 100644 --- a/src/main/java/it/auties/whatsapp/model/button/base/Button.java +++ b/src/main/java/it/auties/whatsapp/model/button/base/Button.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.button.base; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.button.base.ButtonBody.Type; import it.auties.whatsapp.model.info.NativeFlowInfo; @@ -14,7 +13,7 @@ /** * A model class that represents a button */ -@ProtobufMessageName("Message.ButtonsMessage.Button") +@ProtobufMessage(name = "Message.ButtonsMessage.Button") public record Button( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String id, @@ -24,7 +23,7 @@ public record Button( Optional bodyNativeFlow, @ProtobufProperty(index = 3, type = ProtobufType.OBJECT) Type bodyType -) implements ProtobufMessage { +) { /** * Constructs a new button * diff --git a/src/main/java/it/auties/whatsapp/model/button/base/ButtonActionLink.java b/src/main/java/it/auties/whatsapp/model/button/base/ButtonActionLink.java index 3f2e0d727..aa18bcd22 100644 --- a/src/main/java/it/auties/whatsapp/model/button/base/ButtonActionLink.java +++ b/src/main/java/it/auties/whatsapp/model/button/base/ButtonActionLink.java @@ -1,19 +1,18 @@ package it.auties.whatsapp.model.button.base; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; /** * An action link for a button */ -@ProtobufMessageName("ActionLink") +@ProtobufMessage(name = "ActionLink") public record ButtonActionLink( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String url, @ProtobufProperty(index = 2, type = ProtobufType.STRING) String buttonTitle -) implements ProtobufMessage { +) { } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/button/base/ButtonBody.java b/src/main/java/it/auties/whatsapp/model/button/base/ButtonBody.java index 3262ab52f..1092041f3 100644 --- a/src/main/java/it/auties/whatsapp/model/button/base/ButtonBody.java +++ b/src/main/java/it/auties/whatsapp/model/button/base/ButtonBody.java @@ -1,14 +1,13 @@ package it.auties.whatsapp.model.button.base; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.whatsapp.model.info.NativeFlowInfo; /** * A model that represents the body of a button */ -public sealed interface ButtonBody extends ProtobufMessage permits ButtonText, NativeFlowInfo { +public sealed interface ButtonBody permits ButtonText, NativeFlowInfo { /** * Returns the type of this body * @@ -16,7 +15,8 @@ public sealed interface ButtonBody extends ProtobufMessage permits ButtonText, N */ Type bodyType(); - enum Type implements ProtobufEnum { + @ProtobufEnum(name = "Message.ButtonsMessage.Button.Type") + enum Type { UNKNOWN(0), TEXT(1), NATIVE_FLOW(2); diff --git a/src/main/java/it/auties/whatsapp/model/button/misc/ButtonOpaqueData.java b/src/main/java/it/auties/whatsapp/model/button/base/ButtonOpaqueData.java similarity index 92% rename from src/main/java/it/auties/whatsapp/model/button/misc/ButtonOpaqueData.java rename to src/main/java/it/auties/whatsapp/model/button/base/ButtonOpaqueData.java index 5194eee08..4489783fe 100644 --- a/src/main/java/it/auties/whatsapp/model/button/misc/ButtonOpaqueData.java +++ b/src/main/java/it/auties/whatsapp/model/button/base/ButtonOpaqueData.java @@ -1,8 +1,7 @@ -package it.auties.whatsapp.model.button.misc; +package it.auties.whatsapp.model.button.base; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.poll.PollOption; import it.auties.whatsapp.model.poll.PollUpdateEncryptedMetadata; @@ -14,7 +13,7 @@ /** * A model class that represents data about a button */ -@ProtobufMessageName("MsgOpaqueData") +@ProtobufMessage(name = "MsgOpaqueData") public record ButtonOpaqueData( @ProtobufProperty(index = 1, type = ProtobufType.STRING) Optional body, @@ -66,6 +65,6 @@ public record ButtonOpaqueData( Optional encReactionEncPayload, @ProtobufProperty(index = 27, type = ProtobufType.BYTES) Optional encReactionEncIv -) implements ProtobufMessage { +) { } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/button/misc/ButtonRow.java b/src/main/java/it/auties/whatsapp/model/button/base/ButtonRow.java similarity index 74% rename from src/main/java/it/auties/whatsapp/model/button/misc/ButtonRow.java rename to src/main/java/it/auties/whatsapp/model/button/base/ButtonRow.java index 064451fed..dcd3bd210 100644 --- a/src/main/java/it/auties/whatsapp/model/button/misc/ButtonRow.java +++ b/src/main/java/it/auties/whatsapp/model/button/base/ButtonRow.java @@ -1,8 +1,7 @@ -package it.auties.whatsapp.model.button.misc; +package it.auties.whatsapp.model.button.base; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.util.Bytes; @@ -11,7 +10,7 @@ /** * A model class that represents a row of buttons */ -@ProtobufMessageName("Message.ListMessage.Row") +@ProtobufMessage(name = "Message.ListMessage.Row") public record ButtonRow( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String title, @@ -19,7 +18,7 @@ public record ButtonRow( String description, @ProtobufProperty(index = 3, type = ProtobufType.STRING) String id -) implements ProtobufMessage { +) { public static ButtonRow of(String title, String description) { return new ButtonRow(title, description, HexFormat.of().formatHex(Bytes.random(5))); } diff --git a/src/main/java/it/auties/whatsapp/model/button/misc/ButtonRowOpaqueData.java b/src/main/java/it/auties/whatsapp/model/button/base/ButtonRowOpaqueData.java similarity index 66% rename from src/main/java/it/auties/whatsapp/model/button/misc/ButtonRowOpaqueData.java rename to src/main/java/it/auties/whatsapp/model/button/base/ButtonRowOpaqueData.java index 70ea6c85d..204971155 100644 --- a/src/main/java/it/auties/whatsapp/model/button/misc/ButtonRowOpaqueData.java +++ b/src/main/java/it/auties/whatsapp/model/button/base/ButtonRowOpaqueData.java @@ -1,8 +1,7 @@ -package it.auties.whatsapp.model.button.misc; +package it.auties.whatsapp.model.button.base; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.Optional; @@ -10,12 +9,12 @@ /** * A model class that represents data about a row */ -@ProtobufMessageName("MsgRowOpaqueData") +@ProtobufMessage(name = "MsgRowOpaqueData") public record ButtonRowOpaqueData( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) Optional currentMessage, @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) Optional quotedMessage -) implements ProtobufMessage { +) { } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/button/misc/ButtonSection.java b/src/main/java/it/auties/whatsapp/model/button/base/ButtonSection.java similarity index 63% rename from src/main/java/it/auties/whatsapp/model/button/misc/ButtonSection.java rename to src/main/java/it/auties/whatsapp/model/button/base/ButtonSection.java index f783d710d..49d5459d0 100644 --- a/src/main/java/it/auties/whatsapp/model/button/misc/ButtonSection.java +++ b/src/main/java/it/auties/whatsapp/model/button/base/ButtonSection.java @@ -1,8 +1,7 @@ -package it.auties.whatsapp.model.button.misc; +package it.auties.whatsapp.model.button.base; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.List; @@ -10,12 +9,12 @@ /** * A model class that represents a section of buttons */ -@ProtobufMessageName("Message.ListMessage.Section") +@ProtobufMessage(name = "Message.ListMessage.Section") public record ButtonSection( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String title, @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) List rows -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/button/base/ButtonText.java b/src/main/java/it/auties/whatsapp/model/button/base/ButtonText.java index 3d5347fad..34582de55 100644 --- a/src/main/java/it/auties/whatsapp/model/button/base/ButtonText.java +++ b/src/main/java/it/auties/whatsapp/model/button/base/ButtonText.java @@ -1,11 +1,13 @@ package it.auties.whatsapp.model.button.base; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; /** * A model class that represents the text of a button */ +@ProtobufMessage public record ButtonText( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String content diff --git a/src/main/java/it/auties/whatsapp/model/button/misc/SingleSelectReplyButton.java b/src/main/java/it/auties/whatsapp/model/button/base/SingleSelectReplyButton.java similarity index 53% rename from src/main/java/it/auties/whatsapp/model/button/misc/SingleSelectReplyButton.java rename to src/main/java/it/auties/whatsapp/model/button/base/SingleSelectReplyButton.java index 2d705957f..999e32fc5 100644 --- a/src/main/java/it/auties/whatsapp/model/button/misc/SingleSelectReplyButton.java +++ b/src/main/java/it/auties/whatsapp/model/button/base/SingleSelectReplyButton.java @@ -1,17 +1,16 @@ -package it.auties.whatsapp.model.button.misc; +package it.auties.whatsapp.model.button.base; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; /** * A model class that represents the selection of a row */ -@ProtobufMessageName("Message.ListResponseMessage.SingleSelectReply") +@ProtobufMessage(name = "Message.ListResponseMessage.SingleSelectReply") public record SingleSelectReplyButton( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String rowId -) implements ProtobufMessage { +) { } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveBody.java b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveBody.java index 254e87607..01877e035 100644 --- a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveBody.java +++ b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveBody.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.button.interactive; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.Optional; @@ -10,11 +9,11 @@ /** * A model class that represents the body of a product */ -@ProtobufMessageName("Message.InteractiveMessage.Body") +@ProtobufMessage(name = "Message.InteractiveMessage.Body") public record InteractiveBody( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String content -) implements ProtobufMessage { +) { public static Optional ofNullable(String content) { return Optional.ofNullable(content) .map(InteractiveBody::new); diff --git a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveButton.java b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveButton.java index 8504b59d8..6dee40673 100644 --- a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveButton.java +++ b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveButton.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.button.interactive; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.Optional; @@ -10,13 +9,17 @@ /** * A model class that represents a native flow button */ -@ProtobufMessageName("Message.InteractiveMessage.NativeFlowMessage.NativeFlowButton") +@ProtobufMessage(name = "Message.InteractiveMessage.NativeFlowMessage.NativeFlowButton") public record InteractiveButton( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String name, @ProtobufProperty(index = 2, type = ProtobufType.STRING) Optional parameters -) implements ProtobufMessage { +) { + public InteractiveButton(String name, String parameters) { + this(name, Optional.ofNullable(parameters)); + } + public InteractiveButton(String name) { this(name, Optional.empty()); } diff --git a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveCollection.java b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveCollection.java index 87ebfdc25..189347b6d 100644 --- a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveCollection.java +++ b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveCollection.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.button.interactive; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.jid.Jid; @@ -9,7 +9,7 @@ /** * A model class that represents a business collection */ -@ProtobufMessageName("Message.InteractiveMessage.CollectionMessage") +@ProtobufMessage(name = "Message.InteractiveMessage.CollectionMessage") public record InteractiveCollection( @ProtobufProperty(index = 1, type = ProtobufType.STRING) Jid business, diff --git a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveFooter.java b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveFooter.java index 862d56e27..5c8aba10e 100644 --- a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveFooter.java +++ b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveFooter.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.button.interactive; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.Optional; @@ -10,11 +9,11 @@ /** * A model class that represents the footer of a product */ -@ProtobufMessageName("Message.InteractiveMessage.Footer") +@ProtobufMessage(name = "Message.InteractiveMessage.Footer") public record InteractiveFooter( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String content -) implements ProtobufMessage { +) { public static Optional ofNullable(String content) { return Optional.ofNullable(content) diff --git a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveHeader.java b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveHeader.java index 37d853791..d28fe914f 100644 --- a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveHeader.java +++ b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveHeader.java @@ -1,9 +1,8 @@ package it.auties.whatsapp.model.button.interactive; import it.auties.protobuf.annotation.ProtobufBuilder; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.message.standard.DocumentMessage; import it.auties.whatsapp.model.message.standard.ImageMessage; @@ -15,7 +14,7 @@ /** * A model class that represents the header of a product */ -@ProtobufMessageName("Message.InteractiveMessage.Header") +@ProtobufMessage(name = "Message.InteractiveMessage.Header") public record InteractiveHeader( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String title, @@ -31,7 +30,7 @@ public record InteractiveHeader( Optional attachmentThumbnail, @ProtobufProperty(index = 7, type = ProtobufType.OBJECT) Optional attachmentVideo -) implements ProtobufMessage { +) { @ProtobufBuilder(className = "InteractiveHeaderSimpleBuilder") static InteractiveHeader simpleBuilder(String title, String subtitle, InteractiveHeaderAttachment attachment) { var builder = new InteractiveHeaderBuilder() diff --git a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveHeaderAttachment.java b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveHeaderAttachment.java index 703db8df5..81d6e795d 100644 --- a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveHeaderAttachment.java +++ b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveHeaderAttachment.java @@ -1,7 +1,7 @@ package it.auties.whatsapp.model.button.interactive; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; import it.auties.whatsapp.model.message.standard.DocumentMessage; import it.auties.whatsapp.model.message.standard.ImageMessage; import it.auties.whatsapp.model.message.standard.VideoOrGifMessage; @@ -16,7 +16,8 @@ public sealed interface InteractiveHeaderAttachment permits DocumentMessage, Ima * The constants of this enumerated type describe the various types of attachment that a product * header can have */ - enum Type implements ProtobufEnum { + @ProtobufEnum + enum Type { /** * No attachment */ diff --git a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveHeaderThumbnail.java b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveHeaderThumbnail.java index 9064d1695..35418c0f7 100644 --- a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveHeaderThumbnail.java +++ b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveHeaderThumbnail.java @@ -1,6 +1,7 @@ package it.auties.whatsapp.model.button.interactive; -import it.auties.protobuf.annotation.ProtobufConverter; +import it.auties.protobuf.annotation.ProtobufDeserializer; +import it.auties.protobuf.annotation.ProtobufSerializer; /** * A model that represents the jpeg thumbnail of a {@link InteractiveHeader} @@ -8,12 +9,12 @@ * @param thumbnail the non-null jpeg thumbnail */ public record InteractiveHeaderThumbnail(byte[] thumbnail) implements InteractiveHeaderAttachment { - @ProtobufConverter + @ProtobufDeserializer(builderBehaviour = ProtobufDeserializer.BuilderBehaviour.DISCARD) public static InteractiveHeaderThumbnail of(byte[] thumbnail) { return new InteractiveHeaderThumbnail(thumbnail); } - @ProtobufConverter + @ProtobufSerializer @Override public byte[] thumbnail() { return thumbnail; diff --git a/src/main/java/it/auties/whatsapp/model/button/interactive/InterativeLocation.java b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveLocation.java similarity index 69% rename from src/main/java/it/auties/whatsapp/model/button/interactive/InterativeLocation.java rename to src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveLocation.java index f9516393a..98546667f 100644 --- a/src/main/java/it/auties/whatsapp/model/button/interactive/InterativeLocation.java +++ b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveLocation.java @@ -1,21 +1,20 @@ package it.auties.whatsapp.model.button.interactive; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; /** * This model class describes a Location */ -@ProtobufMessageName("Location") -public record InterativeLocation( +@ProtobufMessage(name = "Location") +public record InteractiveLocation( @ProtobufProperty(index = 1, type = ProtobufType.DOUBLE) double latitude, @ProtobufProperty(index = 2, type = ProtobufType.DOUBLE) double longitude, @ProtobufProperty(index = 3, type = ProtobufType.STRING) String name -) implements ProtobufMessage { +) { } 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 935a9d1d1..f01f397c6 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 @@ -1,10 +1,9 @@ package it.auties.whatsapp.model.button.interactive; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.List; @@ -12,13 +11,13 @@ /** * A model class that describes an interactive annotation linked to a message */ -@ProtobufMessageName("InteractiveAnnotation") +@ProtobufMessage(name = "InteractiveAnnotation") public record InteractiveLocationAnnotation( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) List polygonVertices, @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) - InterativeLocation location -) implements ProtobufMessage { + InteractiveLocation location +) { /** * Returns the type of sync * @@ -32,7 +31,8 @@ public Action type() { * The constants of this enumerated type describe the various types of sync that an interactive * annotation can provide */ - public enum Action implements ProtobufEnum { + @ProtobufEnum + public enum Action { /** * Unknown */ 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 4ff9296a6..54bbf0d21 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 @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.button.interactive; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.message.button.InteractiveMessageContent; @@ -12,7 +12,7 @@ * A model class that represents a native flow * Here> is an explanation on how to use this kind of message */ -@ProtobufMessageName("Message.InteractiveMessage.NativeFlowMessage") +@ProtobufMessage(name = "Message.InteractiveMessage.NativeFlowMessage") public record InteractiveNativeFlow( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) List buttons, diff --git a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractivePoint.java b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractivePoint.java index accd9f006..0be3124f6 100644 --- a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractivePoint.java +++ b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractivePoint.java @@ -1,14 +1,13 @@ package it.auties.whatsapp.model.button.interactive; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; /** * This model class describes a Point in space */ -@ProtobufMessageName("Point") +@ProtobufMessage(name = "Point") public record InteractivePoint( @ProtobufProperty(index = 1, type = ProtobufType.INT32) @Deprecated @@ -20,6 +19,6 @@ public record InteractivePoint( double x, @ProtobufProperty(index = 4, type = ProtobufType.DOUBLE) double y -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveResponseBody.java b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveResponseBody.java index b1f81d65f..e98e48119 100644 --- a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveResponseBody.java +++ b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveResponseBody.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.button.interactive; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.Optional; @@ -10,11 +9,11 @@ /** * A model class that represents the body of a product */ -@ProtobufMessageName("Message.InteractiveResponseMessage.Body") +@ProtobufMessage(name = "Message.InteractiveResponseMessage.Body") public record InteractiveResponseBody( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String content -) implements ProtobufMessage { +) { public static Optional ofNullable(String content) { return Optional.ofNullable(content) .map(InteractiveResponseBody::new); diff --git a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveShop.java b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveShop.java index bac46fb0f..34b85adc4 100644 --- a/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveShop.java +++ b/src/main/java/it/auties/whatsapp/model/button/interactive/InteractiveShop.java @@ -1,9 +1,9 @@ package it.auties.whatsapp.model.button.interactive; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.message.button.InteractiveMessageContent; @@ -11,7 +11,7 @@ /** * A model class that represents a shop */ -@ProtobufMessageName("Message.InteractiveMessage.ShopMessage") +@ProtobufMessage(name = "Message.InteractiveMessage.ShopMessage") public record InteractiveShop( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String id, @@ -29,8 +29,8 @@ public Type contentType() { * The constants of this enumerated type describe the various types of surfaces that a * {@link InteractiveShop} can have */ - @ProtobufMessageName("Message.InteractiveMessage.ShopMessage.Surface") - public enum SurfaceType implements ProtobufEnum { + @ProtobufEnum(name = "Message.InteractiveMessage.ShopMessage.Surface") + public enum SurfaceType { /** * Unknown */ diff --git a/src/main/java/it/auties/whatsapp/model/button/template/TemplateFormatter.java b/src/main/java/it/auties/whatsapp/model/button/template/TemplateFormatter.java index 7e7eb646e..996b2b50a 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/TemplateFormatter.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/TemplateFormatter.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.button.template; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.whatsapp.model.button.template.hsm.HighlyStructuredFourRowTemplate; import it.auties.whatsapp.model.button.template.hydrated.HydratedFourRowTemplate; import it.auties.whatsapp.model.message.button.InteractiveMessage; @@ -11,7 +10,7 @@ /** * A formatter used to structure a button message */ -public sealed interface TemplateFormatter extends ProtobufMessage permits HighlyStructuredFourRowTemplate, HydratedFourRowTemplate, InteractiveMessage { +public sealed interface TemplateFormatter permits HighlyStructuredFourRowTemplate, HydratedFourRowTemplate, InteractiveMessage { /** * Returns the type of this formatter * @@ -23,7 +22,8 @@ public sealed interface TemplateFormatter extends ProtobufMessage permits Highly * The constant of this enumerated type define the various of types of visual formats for a * {@link TemplateMessage} */ - enum Type implements ProtobufEnum { + @ProtobufEnum + enum Type { /** * No format */ diff --git a/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredCurrency.java b/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredCurrency.java index 58b5972d3..9daeafbd8 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredCurrency.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredCurrency.java @@ -1,13 +1,13 @@ package it.auties.whatsapp.model.button.template.highlyStructured; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; /** * A model class that represents a currency */ -@ProtobufMessageName("Message.HighlyStructuredMessage.HSMLocalizableParameter.HSMCurrency") +@ProtobufMessage(name = "Message.HighlyStructuredMessage.HSMLocalizableParameter.HSMCurrency") public record HighlyStructuredCurrency( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String currencyCode, diff --git a/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredDateTime.java b/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredDateTime.java index 1e36c9cd0..a09590784 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredDateTime.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredDateTime.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.button.template.highlyStructured; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; @@ -9,7 +9,7 @@ /** * A model class that represents a time */ -@ProtobufMessageName("Message.HighlyStructuredMessage.HSMLocalizableParameter.HSMDateTime") +@ProtobufMessage(name = "Message.HighlyStructuredMessage.HSMLocalizableParameter.HSMDateTime") public record HighlyStructuredDateTime( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) Optional dateComponent, diff --git a/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredDateTimeComponent.java b/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredDateTimeComponent.java index 2647325dc..511da3180 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredDateTimeComponent.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredDateTimeComponent.java @@ -1,15 +1,15 @@ package it.auties.whatsapp.model.button.template.highlyStructured; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; import it.auties.protobuf.model.ProtobufType; /** * A model class that represents a time component */ -@ProtobufMessageName("Message.HighlyStructuredMessage.HSMLocalizableParameter.HSMDateTime.HSMDateTimeComponent") +@ProtobufMessage(name = "Message.HighlyStructuredMessage.HSMLocalizableParameter.HSMDateTime.HSMDateTimeComponent") public record HighlyStructuredDateTimeComponent( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) DayOfWeek dayOfWeek, @@ -34,8 +34,8 @@ public HighlyStructuredDateTimeValue.Type dateType() { /** * The constants of this enumerated type describe the supported calendar types */ - @ProtobufMessageName("Message.HighlyStructuredMessage.HSMLocalizableParameter.HSMDateTime.HSMDateTimeComponent.CalendarType") - public enum CalendarType implements ProtobufEnum { + @ProtobufEnum(name = "Message.HighlyStructuredMessage.HSMLocalizableParameter.HSMDateTime.HSMDateTimeComponent.CalendarType") + public enum CalendarType { /** * Gregorian calendar */ @@ -59,8 +59,9 @@ public int index() { /** * The constants of this enumerated type describe the days of the week */ - @ProtobufMessageName("Message.HighlyStructuredMessage.HSMLocalizableParameter.HSMDateTime.HSMDateTimeComponent.DayOfWeekType") - public enum DayOfWeek implements ProtobufEnum { + @ProtobufEnum(name = "Message.HighlyStructuredMessage.HSMLocalizableParameter.HSMDateTime.HSMDateTimeComponent.DayOfWeekType") + public enum DayOfWeek { + /** * Monday */ diff --git a/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredDateTimeUnixEpoch.java b/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredDateTimeUnixEpoch.java index dd22ee7cb..fc1e2ab65 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredDateTimeUnixEpoch.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredDateTimeUnixEpoch.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.button.template.highlyStructured; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.util.Clock; @@ -11,7 +11,7 @@ /** * A model class that represents a time as a unix epoch */ -@ProtobufMessageName("Message.HighlyStructuredMessage.HSMLocalizableParameter.HSMDateTime.HSMDateTimeUnixEpoch") +@ProtobufMessage(name = "Message.HighlyStructuredMessage.HSMLocalizableParameter.HSMDateTime.HSMDateTimeUnixEpoch") public record HighlyStructuredDateTimeUnixEpoch( @ProtobufProperty(index = 1, type = ProtobufType.INT64) long timestampSeconds diff --git a/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredDateTimeValue.java b/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredDateTimeValue.java index 948ccc59d..0f0dffe32 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredDateTimeValue.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredDateTimeValue.java @@ -1,13 +1,12 @@ package it.auties.whatsapp.model.button.template.highlyStructured; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; /** * A model class that represents the value of a localizable parameter */ -public sealed interface HighlyStructuredDateTimeValue extends ProtobufMessage permits HighlyStructuredDateTimeComponent, HighlyStructuredDateTimeUnixEpoch { +public sealed interface HighlyStructuredDateTimeValue permits HighlyStructuredDateTimeComponent, HighlyStructuredDateTimeUnixEpoch { /** * Returns the type of date * @@ -19,7 +18,8 @@ public sealed interface HighlyStructuredDateTimeValue extends ProtobufMessage pe /** * The constants of this enumerated type describe the various type of date types that a date time can wrap */ - enum Type implements ProtobufEnum { + @ProtobufEnum + enum Type { /** * No date */ diff --git a/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredLocalizableParameter.java b/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredLocalizableParameter.java index 52429099f..c522813a7 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredLocalizableParameter.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredLocalizableParameter.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.button.template.highlyStructured; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.Optional; @@ -10,7 +9,7 @@ /** * A model class that represents a time a localizable parameter */ -@ProtobufMessageName("Message.HighlyStructuredMessage.HSMLocalizableParameter") +@ProtobufMessage(name = "Message.HighlyStructuredMessage.HSMLocalizableParameter") public record HighlyStructuredLocalizableParameter( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String defaultValue, @@ -18,7 +17,7 @@ public record HighlyStructuredLocalizableParameter( Optional parameterCurrency, @ProtobufProperty(index = 3, type = ProtobufType.OBJECT) Optional parameterDateTime -) implements ProtobufMessage { +) { /** * Constructs a new localizable parameter with a default value and a parameter * diff --git a/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredLocalizableParameterValue.java b/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredLocalizableParameterValue.java index f6ce2d48f..6dadc4383 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredLocalizableParameterValue.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/highlyStructured/HighlyStructuredLocalizableParameterValue.java @@ -1,13 +1,12 @@ package it.auties.whatsapp.model.button.template.highlyStructured; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; /** * A model class that represents the value of a localizable parameter */ -public sealed interface HighlyStructuredLocalizableParameterValue extends ProtobufMessage permits HighlyStructuredCurrency, HighlyStructuredDateTime { +public sealed interface HighlyStructuredLocalizableParameterValue permits HighlyStructuredCurrency, HighlyStructuredDateTime { /** * Returns the type of parameter * @@ -15,7 +14,8 @@ public sealed interface HighlyStructuredLocalizableParameterValue extends Protob */ Type parameterType(); - enum Type implements ProtobufEnum { + @ProtobufEnum + enum Type { /** * No parameter */ 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 8084d7aaa..c9e107c51 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 @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.button.template.highlyStructured; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.button.template.hsm.HighlyStructuredFourRowTemplateTitle; @@ -15,7 +15,7 @@ * A model class that represents a message that contains a highly structured message inside. Not * really clear how this could be used, contributions are welcomed. */ -@ProtobufMessageName("Message.HighlyStructuredMessage") +@ProtobufMessage(name = "Message.HighlyStructuredMessage") public record HighlyStructuredMessage( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String namespace, diff --git a/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredButton.java b/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredButton.java index 44a2e8f38..4eb3e84a1 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredButton.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredButton.java @@ -1,14 +1,13 @@ package it.auties.whatsapp.model.button.template.hsm; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.whatsapp.model.button.template.highlyStructured.HighlyStructuredMessage; /** * A model that represents all types of hydrated buttons */ -public sealed interface HighlyStructuredButton extends ProtobufMessage permits HighlyStructuredCallButton, HighlyStructuredQuickReplyButton, HighlyStructuredURLButton { +public sealed interface HighlyStructuredButton permits HighlyStructuredCallButton, HighlyStructuredQuickReplyButton, HighlyStructuredURLButton { /** * Returns the text of this button * @@ -27,7 +26,8 @@ public sealed interface HighlyStructuredButton extends ProtobufMessage permits H * The constants of this enumerated type describe the various types of buttons that a template can * wrap */ - enum Type implements ProtobufEnum { + @ProtobufEnum + enum Type { /** * No button */ diff --git a/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredButtonTemplate.java b/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredButtonTemplate.java index a7e335ff6..b2f23275b 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredButtonTemplate.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredButtonTemplate.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.button.template.hsm; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.Optional; @@ -10,7 +9,7 @@ /** * A model class that represents a template for a button */ -@ProtobufMessageName("HydratedTemplateButton") +@ProtobufMessage(name = "HydratedTemplateButton") public record HighlyStructuredButtonTemplate( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) Optional highlyStructuredQuickReplyButton, @@ -20,7 +19,7 @@ public record HighlyStructuredButtonTemplate( Optional highlyStructuredCallButton, @ProtobufProperty(index = 4, type = ProtobufType.UINT32) int index -) implements ProtobufMessage { +) { /** * Constructs a new template * diff --git a/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredCallButton.java b/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredCallButton.java index f3a1c2d82..5d503c7f7 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredCallButton.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredCallButton.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.button.template.hsm; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.button.template.highlyStructured.HighlyStructuredMessage; @@ -8,7 +8,7 @@ /** * A model class that represents a button that can start a phone call */ -@ProtobufMessageName("TemplateButton.CallButton") +@ProtobufMessage(name = "TemplateButton.CallButton") public record HighlyStructuredCallButton( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) HighlyStructuredMessage text, 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 1518b0a5b..1ecab3713 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 @@ -1,7 +1,7 @@ package it.auties.whatsapp.model.button.template.hsm; import it.auties.protobuf.annotation.ProtobufBuilder; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.button.template.TemplateFormatter; @@ -18,7 +18,7 @@ /** * A model class that represents a four row template */ -@ProtobufMessageName("Message.TemplateMessage.FourRowTemplate") +@ProtobufMessage(name = "Message.TemplateMessage.FourRowTemplate") public record HighlyStructuredFourRowTemplate( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) Optional titleDocument, diff --git a/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredFourRowTemplateTitle.java b/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredFourRowTemplateTitle.java index 877f0a8d9..be340bf2a 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredFourRowTemplateTitle.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredFourRowTemplateTitle.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.button.template.hsm; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.whatsapp.model.button.template.highlyStructured.HighlyStructuredMessage; import it.auties.whatsapp.model.message.standard.DocumentMessage; import it.auties.whatsapp.model.message.standard.ImageMessage; @@ -12,7 +11,7 @@ /** * A model that represents the title of a {@link HighlyStructuredFourRowTemplate} */ -public sealed interface HighlyStructuredFourRowTemplateTitle extends ProtobufMessage permits DocumentMessage, HighlyStructuredMessage, ImageMessage, VideoOrGifMessage, LocationMessage { +public sealed interface HighlyStructuredFourRowTemplateTitle permits DocumentMessage, HighlyStructuredMessage, ImageMessage, VideoOrGifMessage, LocationMessage { /** * Return the type of this title * @@ -24,7 +23,8 @@ public sealed interface HighlyStructuredFourRowTemplateTitle extends ProtobufMes * The constants of this enumerated type describe the various types of title that a template can * have */ - enum Type implements ProtobufEnum { + @ProtobufEnum + enum Type { /** * No title */ diff --git a/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredQuickReplyButton.java b/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredQuickReplyButton.java index 16087a075..06d05400f 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredQuickReplyButton.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredQuickReplyButton.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.button.template.hsm; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.button.template.highlyStructured.HighlyStructuredMessage; @@ -8,7 +8,7 @@ /** * A model class that represents a quick reply button */ -@ProtobufMessageName("TemplateButton.QuickReplyButton") +@ProtobufMessage(name = "TemplateButton.QuickReplyButton") public record HighlyStructuredQuickReplyButton( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) HighlyStructuredMessage text, diff --git a/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredURLButton.java b/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredURLButton.java index 12e8f2f50..61661684e 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredURLButton.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/hsm/HighlyStructuredURLButton.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.button.template.hsm; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.button.template.highlyStructured.HighlyStructuredMessage; @@ -8,7 +8,7 @@ /** * A model class that represents an url button */ -@ProtobufMessageName("TemplateButton.URLButton") +@ProtobufMessage(name = "TemplateButton.URLButton") public record HighlyStructuredURLButton( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) HighlyStructuredMessage text, diff --git a/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedButton.java b/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedButton.java index d93381639..956775a80 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedButton.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedButton.java @@ -1,13 +1,12 @@ package it.auties.whatsapp.model.button.template.hydrated; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; /** * A model that represents all types of hydrated buttons */ -public sealed interface HydratedButton extends ProtobufMessage permits HydratedCallButton, HydratedQuickReplyButton, HydratedURLButton { +public sealed interface HydratedButton permits HydratedCallButton, HydratedQuickReplyButton, HydratedURLButton { /** * Returns the text of this button * @@ -26,7 +25,8 @@ public sealed interface HydratedButton extends ProtobufMessage permits HydratedC * The constants of this enumerated type describe the various types of buttons that a template can * wrap */ - enum Type implements ProtobufEnum { + @ProtobufEnum + enum Type { /** * No button */ diff --git a/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedCallButton.java b/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedCallButton.java index 53d3002df..e4762cdf8 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedCallButton.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedCallButton.java @@ -1,13 +1,13 @@ package it.auties.whatsapp.model.button.template.hydrated; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; /** * A model class that represents a hydrated button that can start a phone call */ -@ProtobufMessageName("HydratedTemplateButton.HydratedCallButton") +@ProtobufMessage(name = "HydratedTemplateButton.HydratedCallButton") public record HydratedCallButton( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String text, 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 a7fe75c25..f78bdd997 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 @@ -1,7 +1,7 @@ package it.auties.whatsapp.model.button.template.hydrated; import it.auties.protobuf.annotation.ProtobufBuilder; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.button.template.TemplateFormatter; @@ -17,7 +17,7 @@ /** * A model class that represents a hydrated four row template */ -@ProtobufMessageName("Message.TemplateMessage.HydratedFourRowTemplate") +@ProtobufMessage(name = "Message.TemplateMessage.HydratedFourRowTemplate") public record HydratedFourRowTemplate( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) Optional titleDocument, diff --git a/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedFourRowTemplateTextTitle.java b/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedFourRowTemplateTextTitle.java index fb3a18474..1966c1b35 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedFourRowTemplateTextTitle.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedFourRowTemplateTextTitle.java @@ -1,6 +1,7 @@ package it.auties.whatsapp.model.button.template.hydrated; -import it.auties.protobuf.annotation.ProtobufConverter; +import it.auties.protobuf.annotation.ProtobufDeserializer; +import it.auties.protobuf.annotation.ProtobufSerializer; /** * A model class that represents a hydrated four row template @@ -8,12 +9,12 @@ public record HydratedFourRowTemplateTextTitle( String text ) implements HydratedFourRowTemplateTitle { - @ProtobufConverter + @ProtobufDeserializer public static HydratedFourRowTemplateTextTitle of(String text) { return new HydratedFourRowTemplateTextTitle(text); } - @ProtobufConverter + @ProtobufSerializer public String text() { return text; } diff --git a/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedFourRowTemplateTitle.java b/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedFourRowTemplateTitle.java index 81b7e012a..5c95964af 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedFourRowTemplateTitle.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedFourRowTemplateTitle.java @@ -1,7 +1,7 @@ package it.auties.whatsapp.model.button.template.hydrated; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; import it.auties.whatsapp.model.message.standard.DocumentMessage; import it.auties.whatsapp.model.message.standard.ImageMessage; import it.auties.whatsapp.model.message.standard.LocationMessage; @@ -22,7 +22,8 @@ public sealed interface HydratedFourRowTemplateTitle permits DocumentMessage, Hy * The constants of this enumerated type describe the various types of title that a template can * wrap */ - enum Type implements ProtobufEnum { + @ProtobufEnum + enum Type { /** * No title */ diff --git a/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedQuickReplyButton.java b/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedQuickReplyButton.java index db8c78462..27cbab66b 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedQuickReplyButton.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedQuickReplyButton.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.button.template.hydrated; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.util.Bytes; @@ -10,7 +10,7 @@ /** * A model class that represents a hydrated quick reply button */ -@ProtobufMessageName("HydratedTemplateButton.HydratedQuickReplyButton") +@ProtobufMessage(name = "HydratedTemplateButton.HydratedQuickReplyButton") public record HydratedQuickReplyButton( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String text, diff --git a/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedTemplateButton.java b/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedTemplateButton.java index 48cedc517..27df91ffd 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedTemplateButton.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedTemplateButton.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.button.template.hydrated; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.Optional; @@ -10,7 +9,7 @@ /** * A model class that represents a hydrated template for a button */ -@ProtobufMessageName("HydratedTemplateButton") +@ProtobufMessage(name = "HydratedTemplateButton") public record HydratedTemplateButton( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) HydratedQuickReplyButton quickReplyButton, @@ -20,7 +19,7 @@ public record HydratedTemplateButton( HydratedCallButton callButton, @ProtobufProperty(index = 4, type = ProtobufType.UINT32) int index -) implements ProtobufMessage { +) { /** * Constructs a new template button * diff --git a/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedURLButton.java b/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedURLButton.java index 5365f21fe..f4da7649d 100644 --- a/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedURLButton.java +++ b/src/main/java/it/auties/whatsapp/model/button/template/hydrated/HydratedURLButton.java @@ -1,13 +1,13 @@ package it.auties.whatsapp.model.button.template.hydrated; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; /** * A model class that represents a hydrated url button */ -@ProtobufMessageName("HydratedTemplateButton.HydratedURLButton") +@ProtobufMessage(name = "HydratedTemplateButton.HydratedURLButton") public record HydratedURLButton( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String text, 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 e592e4566..3dbdfa6c3 100644 --- a/src/main/java/it/auties/whatsapp/model/call/Call.java +++ b/src/main/java/it/auties/whatsapp/model/call/Call.java @@ -1,10 +1,11 @@ package it.auties.whatsapp.model.call; +import it.auties.protobuf.annotation.ProtobufMessage; 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; +@ProtobufMessage public record Call( @ProtobufProperty(index = 1, type = ProtobufType.STRING) Jid chat, @@ -20,6 +21,6 @@ public record Call( CallStatus status, @ProtobufProperty(index = 7, type = ProtobufType.BOOL) boolean offline -) implements ProtobufMessage { +) { } \ 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 fd4cc1553..b00d94b18 100644 --- a/src/main/java/it/auties/whatsapp/model/call/CallStatus.java +++ b/src/main/java/it/auties/whatsapp/model/call/CallStatus.java @@ -1,9 +1,10 @@ package it.auties.whatsapp.model.call; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; -public enum CallStatus implements ProtobufEnum { +@ProtobufEnum +public enum CallStatus { RINGING(0), ACCEPTED(1), REJECTED(2), 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 62f8fe314..82f2bbf9e 100644 --- a/src/main/java/it/auties/whatsapp/model/chat/Chat.java +++ b/src/main/java/it/auties/whatsapp/model/chat/Chat.java @@ -1,11 +1,10 @@ package it.auties.whatsapp.model.chat; import com.fasterxml.jackson.annotation.JsonCreator; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.api.Whatsapp; import it.auties.whatsapp.model.contact.ContactStatus; @@ -17,7 +16,7 @@ import it.auties.whatsapp.model.message.model.MessageCategory; import it.auties.whatsapp.model.sync.HistorySyncMessage; import it.auties.whatsapp.util.Clock; -import it.auties.whatsapp.util.ConcurrentLinkedHashedDequeue; +import it.auties.whatsapp.util.ConcurrentLinkedSet; import java.time.Instant; import java.time.ZonedDateTime; @@ -31,136 +30,96 @@ * or a group. This class is only a model, this means that changing its values will have no real * effect on WhatsappWeb's servers */ -@ProtobufMessageName("Conversation") -public final class Chat implements ProtobufMessage, JidProvider { +@ProtobufMessage(name = "Conversation") +public final class Chat implements JidProvider { @ProtobufProperty(index = 1, type = ProtobufType.STRING) final Jid jid; - @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) - final ConcurrentLinkedHashedDequeue historySyncMessages; - + final ConcurrentLinkedSet historySyncMessages; @ProtobufProperty(index = 3, type = ProtobufType.STRING) final Jid newJid; - @ProtobufProperty(index = 4, type = ProtobufType.STRING) final Jid oldJid; - @ProtobufProperty(index = 6, type = ProtobufType.UINT32) int unreadMessagesCount; - @ProtobufProperty(index = 7, type = ProtobufType.BOOL) boolean readOnly; - @ProtobufProperty(index = 8, type = ProtobufType.BOOL) boolean endOfHistoryTransfer; - @ProtobufProperty(index = 9, type = ProtobufType.UINT32) ChatEphemeralTimer ephemeralMessageDuration; - @ProtobufProperty(index = 10, type = ProtobufType.INT64) long ephemeralMessagesToggleTimeSeconds; - @ProtobufProperty(index = 11, type = ProtobufType.OBJECT) EndOfHistoryTransferType endOfHistoryTransferType; - @ProtobufProperty(index = 12, type = ProtobufType.UINT64) long timestampSeconds; - @ProtobufProperty(index = 13, type = ProtobufType.STRING) String name; - @ProtobufProperty(index = 15, type = ProtobufType.BOOL) boolean notSpam; - @ProtobufProperty(index = 16, type = ProtobufType.BOOL) boolean archived; @ProtobufProperty(index = 17, type = ProtobufType.OBJECT) ChatDisappear disappearInitiator; - @ProtobufProperty(index = 19, type = ProtobufType.BOOL) boolean markedAsUnread; - @ProtobufProperty(index = 20, type = ProtobufType.OBJECT) final List participants; - @ProtobufProperty(index = 21, type = ProtobufType.BYTES) byte[] token; - @ProtobufProperty(index = 22, type = ProtobufType.UINT64) long tokenTimestampSeconds; - @ProtobufProperty(index = 23, type = ProtobufType.BYTES) byte[] identityKey; - @ProtobufProperty(index = 24, type = ProtobufType.UINT32) int pinnedTimestampSeconds; - @ProtobufProperty(index = 25, type = ProtobufType.UINT64) ChatMute mute; - @ProtobufProperty(index = 26, type = ProtobufType.OBJECT) ChatWallpaper wallpaper; - @ProtobufProperty(index = 27, type = ProtobufType.OBJECT) MediaVisibility mediaVisibility; - @ProtobufProperty(index = 28, type = ProtobufType.UINT64) long tokenSenderTimestampSeconds; - @ProtobufProperty(index = 29, type = ProtobufType.BOOL) boolean suspended; - @ProtobufProperty(index = 30, type = ProtobufType.BOOL) boolean terminated; - @ProtobufProperty(index = 31, type = ProtobufType.UINT64) long foundationTimestampSeconds; - @ProtobufProperty(index = 32, type = ProtobufType.STRING) Jid founder; @ProtobufProperty(index = 33, type = ProtobufType.STRING) String description; - @ProtobufProperty(index = 34, type = ProtobufType.BOOL) boolean support; - @ProtobufProperty(index = 35, type = ProtobufType.BOOL) boolean parentGroup; - @ProtobufProperty(index = 36, type = ProtobufType.BOOL) boolean defaultSubGroup; - @ProtobufProperty(index = 37, type = ProtobufType.STRING) final Jid parentGroupJid; - @ProtobufProperty(index = 38, type = ProtobufType.STRING) String displayName; - @ProtobufProperty(index = 39, type = ProtobufType.STRING) Jid phoneJid; - @ProtobufProperty(index = 40, type = ProtobufType.BOOL) boolean shareOwnPhoneNumber; - @ProtobufProperty(index = 41, type = ProtobufType.BOOL) boolean pnhDuplicateLidThread; - @ProtobufProperty(index = 42, type = ProtobufType.STRING) Jid lidJid; - - @ProtobufProperty(index = 999, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.OBJECT) + @ProtobufProperty(index = 999, type = ProtobufType.MAP, mapKeyType = ProtobufType.STRING, mapValueType = 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; @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) { + Chat(Jid jid, ConcurrentLinkedSet 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; this.historySyncMessages = historySyncMessages; this.newJid = newJid; @@ -204,7 +163,7 @@ public Chat(Jid jid, ConcurrentLinkedHashedDequeue historySy this.participantsPreKeys = participantsPreKeys; this.pastParticipants = pastParticipants; } - + /** * Returns the name of this chat * @@ -534,7 +493,7 @@ private void refreshChatTimestamp() { } private void updateChatTimestamp(ChatMessageInfo info) { - if(info.timestampSeconds().isEmpty()) { + if (info.timestampSeconds().isEmpty()) { return; } @@ -1112,8 +1071,8 @@ public boolean hasUpdate() { * The constants of this enumerated type describe the various types of transfers that can regard a * chat history sync */ - @ProtobufMessageName("Conversation.EndOfHistoryTransferType") - public enum EndOfHistoryTransferType implements ProtobufEnum { + @ProtobufEnum(name = "Conversation.EndOfHistoryTransferType") + public enum EndOfHistoryTransferType { /** * Complete, but more messages remain on the phone */ diff --git a/src/main/java/it/auties/whatsapp/model/chat/ChatDisappear.java b/src/main/java/it/auties/whatsapp/model/chat/ChatDisappear.java index 8e475b5cb..794a0ca86 100644 --- a/src/main/java/it/auties/whatsapp/model/chat/ChatDisappear.java +++ b/src/main/java/it/auties/whatsapp/model/chat/ChatDisappear.java @@ -1,10 +1,9 @@ package it.auties.whatsapp.model.chat; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.Objects; @@ -12,17 +11,17 @@ /** * A model that represents a chat disappear mode */ -@ProtobufMessageName("DisappearingMode") +@ProtobufMessage(name = "DisappearingMode") public record ChatDisappear( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) Initiator initiator -) implements ProtobufMessage { +) { /** * The constants of this enumerated type describe the various actors that can initialize * disappearing messages in a chat */ - @ProtobufMessageName("DisappearingMode.Initiator") - public enum Initiator implements ProtobufEnum { + @ProtobufEnum(name = "DisappearingMode.Initiator") + public enum Initiator { /** * Changed in chat */ 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 5df95501b..2938e4908 100644 --- a/src/main/java/it/auties/whatsapp/model/chat/ChatEphemeralTimer.java +++ b/src/main/java/it/auties/whatsapp/model/chat/ChatEphemeralTimer.java @@ -1,8 +1,9 @@ package it.auties.whatsapp.model.chat; -import it.auties.protobuf.annotation.ProtobufConverter; +import it.auties.protobuf.annotation.ProtobufDeserializer; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; +import it.auties.protobuf.annotation.ProtobufSerializer; import java.time.Duration; import java.util.Arrays; @@ -11,7 +12,8 @@ * Enum representing the ChatEphemeralTimer period. Each constant is associated with a specific * duration period. */ -public enum ChatEphemeralTimer implements ProtobufEnum { +@ProtobufEnum +public enum ChatEphemeralTimer { /** * ChatEphemeralTimer with duration of 0 days. */ @@ -48,7 +50,7 @@ public Duration period() { return period; } - @ProtobufConverter + @ProtobufDeserializer public static ChatEphemeralTimer of(int value) { return Arrays.stream(values()) .filter(entry -> entry.period().toSeconds() == value || entry.period().toDays() == value) @@ -56,7 +58,7 @@ public static ChatEphemeralTimer of(int value) { .orElse(OFF); } - @ProtobufConverter + @ProtobufSerializer public int periodSeconds() { return (int) period.toSeconds(); } diff --git a/src/main/java/it/auties/whatsapp/model/chat/ChatMute.java b/src/main/java/it/auties/whatsapp/model/chat/ChatMute.java index 9ac07c457..9793534af 100644 --- a/src/main/java/it/auties/whatsapp/model/chat/ChatMute.java +++ b/src/main/java/it/auties/whatsapp/model/chat/ChatMute.java @@ -1,6 +1,7 @@ package it.auties.whatsapp.model.chat; -import it.auties.protobuf.annotation.ProtobufConverter; +import it.auties.protobuf.annotation.ProtobufDeserializer; +import it.auties.protobuf.annotation.ProtobufSerializer; import it.auties.whatsapp.util.Clock; import java.time.Instant; @@ -64,7 +65,7 @@ public static ChatMute mutedForEightHours() { /** * Do not use this method, reserved for protobuf */ - @ProtobufConverter + @ProtobufDeserializer public static ChatMute ofProtobuf(long object) { return muted(object); } @@ -127,7 +128,7 @@ public Optional end() { return Clock.parseSeconds(endTimeStamp); } - @ProtobufConverter + @ProtobufSerializer public long endTimeStamp() { return endTimeStamp; } diff --git a/src/main/java/it/auties/whatsapp/model/chat/ChatWallpaper.java b/src/main/java/it/auties/whatsapp/model/chat/ChatWallpaper.java index c5eeb7d7c..63b58481f 100644 --- a/src/main/java/it/auties/whatsapp/model/chat/ChatWallpaper.java +++ b/src/main/java/it/auties/whatsapp/model/chat/ChatWallpaper.java @@ -1,18 +1,17 @@ package it.auties.whatsapp.model.chat; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; /** * A model class that represents the wallpaper of a chat. */ -@ProtobufMessageName("WallpaperSettings") +@ProtobufMessage(name = "WallpaperSettings") public record ChatWallpaper( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String filename, @ProtobufProperty(index = 2, type = ProtobufType.UINT32) int opacity -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/chat/GroupParticipant.java b/src/main/java/it/auties/whatsapp/model/chat/GroupParticipant.java index 8e3f1dbe4..f137148e1 100644 --- a/src/main/java/it/auties/whatsapp/model/chat/GroupParticipant.java +++ b/src/main/java/it/auties/whatsapp/model/chat/GroupParticipant.java @@ -1,9 +1,8 @@ package it.auties.whatsapp.model.chat; import com.fasterxml.jackson.annotation.JsonCreator; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; 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; @@ -12,8 +11,8 @@ /** * A model class that represents a participant of a group. */ -@ProtobufMessageName("GroupParticipant") -public final class GroupParticipant implements ProtobufMessage { +@ProtobufMessage(name = "GroupParticipant") +public final class GroupParticipant { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private final Jid jid; diff --git a/src/main/java/it/auties/whatsapp/model/chat/GroupPastParticipant.java b/src/main/java/it/auties/whatsapp/model/chat/GroupPastParticipant.java index c168b0819..bafc77bd9 100644 --- a/src/main/java/it/auties/whatsapp/model/chat/GroupPastParticipant.java +++ b/src/main/java/it/auties/whatsapp/model/chat/GroupPastParticipant.java @@ -1,10 +1,9 @@ package it.auties.whatsapp.model.chat; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.jid.Jid; import it.auties.whatsapp.util.Clock; @@ -16,7 +15,7 @@ /** * Class representing a past participant in a chat */ -@ProtobufMessageName("PastParticipant") +@ProtobufMessage(name = "PastParticipant") public record GroupPastParticipant( @ProtobufProperty(index = 1, type = ProtobufType.STRING) Jid jid, @@ -24,7 +23,7 @@ public record GroupPastParticipant( Reason reason, @ProtobufProperty(index = 3, type = ProtobufType.UINT64) long timestampSeconds -) implements ProtobufMessage { +) { /** * Returns when the past participant left the chat @@ -38,8 +37,8 @@ public Optional timestamp() { /** * Enum representing the errorReason for a past participant leaving the chat. */ - @ProtobufMessageName("PastParticipant.LeaveReason") - public enum Reason implements ProtobufEnum { + @ProtobufEnum(name = "PastParticipant.LeaveReason") + public enum Reason { /** * The past participant left the chat voluntarily. */ 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 b60753758..771da5508 100644 --- a/src/main/java/it/auties/whatsapp/model/chat/GroupPastParticipants.java +++ b/src/main/java/it/auties/whatsapp/model/chat/GroupPastParticipants.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.chat; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; 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; @@ -11,12 +10,12 @@ /** * Class representing a list of past participants in a chat group */ -@ProtobufMessageName("PastParticipants") +@ProtobufMessage(name = "PastParticipants") public record GroupPastParticipants( @ProtobufProperty(index = 1, type = ProtobufType.STRING) Jid groupJid, @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) List pastParticipants -) implements ProtobufMessage { +) { } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/chat/GroupRole.java b/src/main/java/it/auties/whatsapp/model/chat/GroupRole.java index f532acb82..abdce5bd8 100644 --- a/src/main/java/it/auties/whatsapp/model/chat/GroupRole.java +++ b/src/main/java/it/auties/whatsapp/model/chat/GroupRole.java @@ -1,8 +1,8 @@ package it.auties.whatsapp.model.chat; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; -import it.auties.protobuf.model.ProtobufEnum; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.whatsapp.api.Whatsapp; import java.util.Arrays; @@ -13,8 +13,8 @@ * The constants of this enumerated type describe the various roles that a {@link GroupParticipant} * can have in a group. Said roles can be changed using various methods in {@link Whatsapp}. */ -@ProtobufMessageName("GroupParticipant.Rank") -public enum GroupRole implements ProtobufEnum { +@ProtobufEnum(name = "GroupParticipant.Rank") +public enum GroupRole { /** * A participant of the group with no special powers */ 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 83f50a1f9..bb4e0f962 100644 --- a/src/main/java/it/auties/whatsapp/model/companion/CompanionDevice.java +++ b/src/main/java/it/auties/whatsapp/model/companion/CompanionDevice.java @@ -1,34 +1,71 @@ package it.auties.whatsapp.model.companion; +import it.auties.protobuf.annotation.ProtobufMessage; 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.signal.auth.UserAgent.PlatformType; import it.auties.whatsapp.model.signal.auth.Version; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; import java.util.Optional; /** * A model for a mobile companion - * - * @param model the non-null model of the device - * @param manufacturer the non-null manufacturer of the device - * @param platform the non-null os of the device - * @param appVersion the version of the app, or empty - * @param osVersion the non-null os version of the device */ -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.OBJECT) - Optional appVersion, - @ProtobufProperty(index = 5, type = ProtobufType.OBJECT) - Version osVersion -) implements ProtobufMessage { +@ProtobufMessage +public final class CompanionDevice { + private static final List> IOS_VERSION = List.of( + Map.entry("17.5.1", "21F91") + ); + + @ProtobufProperty(index = 1, type = ProtobufType.STRING) + private final String model; + + @ProtobufProperty(index = 2, type = ProtobufType.STRING) + private final String manufacturer; + + @ProtobufProperty(index = 3, type = ProtobufType.OBJECT) + private final PlatformType platform; + + @ProtobufProperty(index = 4, type = ProtobufType.OBJECT) + private final Version appVersion; + + @ProtobufProperty(index = 5, type = ProtobufType.OBJECT) + private final Version osVersion; + + @ProtobufProperty(index = 6, type = ProtobufType.STRING) + private final String osBuildNumber; + + @ProtobufProperty(index = 8, type = ProtobufType.STRING) + private final String modelId; + + @ProtobufProperty(index = 9, type = ProtobufType.OBJECT) + private final ClientType clientType; + + CompanionDevice( + String model, + String manufacturer, + PlatformType platform, + Version appVersion, + Version osVersion, + String osBuildNumber, + String modelId, + ClientType clientType + ) { + this.model = model; + this.modelId = modelId; + this.manufacturer = manufacturer; + this.platform = platform; + this.appVersion = appVersion; + this.osVersion = osVersion; + this.osBuildNumber = osBuildNumber; + this.clientType = clientType; + } + public static CompanionDevice web() { return web(null); } @@ -38,8 +75,11 @@ public static CompanionDevice web(Version appVersion) { "Chrome", "Google", PlatformType.WEB, - Optional.ofNullable(appVersion), - Version.of("1.0") + appVersion, + Version.of("1.0"), + null, + null, + ClientType.WEB ); } @@ -52,50 +92,80 @@ public static CompanionDevice ios(Version appVersion, boolean business) { "iPhone_15_Pro_Max", "Apple", business ? PlatformType.IOS_BUSINESS : PlatformType.IOS, - Optional.ofNullable(appVersion), - Version.of("17.4.1") + appVersion, + Version.of("17.2.1"), + "", + "iPhone16,2", + ClientType.MOBILE ); } + public static CompanionDevice android(boolean business) { + return android(null, business); + } + + public static CompanionDevice android(Version appVersion, boolean business) { + return new CompanionDevice( + "Pixel_4", + "Google", + business ? PlatformType.ANDROID_BUSINESS : PlatformType.ANDROID, + appVersion, + Version.of("12"), + null, + "Pixel_4", + ClientType.MOBILE + ); + } + + public String osBuildNumber() { + return Objects.requireNonNullElse(osBuildNumber, osVersion.toString()); + } + public String toUserAgent(Version appVersion) { return "WhatsApp/%s %s/%s Device/%s".formatted( appVersion, platformName(), - osVersion(), + deviceVersion(), deviceName() ); } public CompanionDevice toPersonal() { - if(!platform.isBusiness()) { + if (!platform.isBusiness()) { return this; } - return new CompanionDevice( - model, - manufacturer, - platform.toPersonal(), - appVersion, - osVersion - ); + return withPlatform(platform.toPersonal()); } public CompanionDevice toBusiness() { - if(platform.isBusiness()) { + if (platform.isBusiness()) { return this; } + return withPlatform(platform.toBusiness()); + } + + public CompanionDevice withPlatform(PlatformType platform) { return new CompanionDevice( model, manufacturer, - platform.toBusiness(), + Objects.requireNonNullElse(platform, this.platform), appVersion, - osVersion + osVersion, + osBuildNumber, + modelId, + clientType ); } + private String deviceVersion() { + return osVersion.toString(); + } + private String deviceName() { return switch (platform()) { + case ANDROID, ANDROID_BUSINESS -> manufacturer + "-" + model; case IOS, IOS_BUSINESS -> model; default -> throw new IllegalStateException("Unsupported mobile os"); }; @@ -103,9 +173,66 @@ private String deviceName() { private String platformName() { return switch (platform()) { + case ANDROID -> "Android"; + case ANDROID_BUSINESS -> "SMBA"; case IOS -> "iOS"; case IOS_BUSINESS -> "SMB iOS"; default -> throw new IllegalStateException("Unsupported mobile os"); }; } + + public String model() { + return model; + } + + public String modelId() { + return modelId; + } + + public String manufacturer() { + return manufacturer; + } + + public PlatformType platform() { + return platform; + } + + public Optional appVersion() { + return Optional.ofNullable(appVersion); + } + + public Version osVersion() { + return osVersion; + } + + public ClientType clientType() { + return clientType; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CompanionDevice that = (CompanionDevice) o; + return Objects.equals(model, that.model) && Objects.equals(manufacturer, that.manufacturer) && platform == that.platform && Objects.equals(appVersion, that.appVersion) && Objects.equals(osVersion, that.osVersion) && Objects.equals(osBuildNumber, that.osBuildNumber) && Objects.equals(modelId, that.modelId) && clientType == that.clientType; + } + + @Override + public int hashCode() { + return Objects.hash(model, manufacturer, platform, appVersion, osVersion, osBuildNumber, modelId, clientType); + } + + @Override + public String toString() { + return "CompanionDevice{" + + "model='" + model + '\'' + + ", manufacturer='" + manufacturer + '\'' + + ", platform=" + platform + + ", appVersion=" + appVersion + + ", osVersion=" + osVersion + + ", osBuildNumber='" + osBuildNumber + '\'' + + ", modelId='" + modelId + '\'' + + ", clientType=" + clientType + + '}'; + } } 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 edd440f4b..8b09213a4 100644 --- a/src/main/java/it/auties/whatsapp/model/companion/CompanionHashState.java +++ b/src/main/java/it/auties/whatsapp/model/companion/CompanionHashState.java @@ -1,8 +1,8 @@ package it.auties.whatsapp.model.companion; import com.fasterxml.jackson.annotation.JsonCreator; +import it.auties.protobuf.annotation.ProtobufMessage; 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; @@ -15,7 +15,8 @@ import static it.auties.whatsapp.model.node.Node.of; -public final class CompanionHashState implements ProtobufMessage { +@ProtobufMessage +public final class CompanionHashState { @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) private PatchType type; @@ -25,7 +26,7 @@ public final class CompanionHashState implements ProtobufMessage { @ProtobufProperty(index = 3, type = ProtobufType.BYTES) private byte[] hash; - @ProtobufProperty(index = 4, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.BYTES) + @ProtobufProperty(index = 4, type = ProtobufType.MAP, mapKeyType = ProtobufType.STRING, mapValueType = ProtobufType.BYTES) private Map indexValueMap; public CompanionHashState(PatchType type) { @@ -47,6 +48,11 @@ public CompanionHashState(PatchType type, long version, byte[] hash, Map checkIndexEntryEquality(that, entry.getKey(), entry.getValue())); } - private static boolean checkIndexEntryEquality(CompanionHashState that, String thisKey, byte[] thisValue) { - var thatValue = that.indexValueMap().get(thisKey); - return thatValue != null && Arrays.equals(thatValue, thisValue); - } - public PatchType type() { return this.type; } diff --git a/src/main/java/it/auties/whatsapp/model/companion/CompanionPatch.java b/src/main/java/it/auties/whatsapp/model/companion/CompanionPatch.java deleted file mode 100644 index 24fd3cb78..000000000 --- a/src/main/java/it/auties/whatsapp/model/companion/CompanionPatch.java +++ /dev/null @@ -1,15 +0,0 @@ -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 index e9c74f38b..7e7779ecc 100644 --- a/src/main/java/it/auties/whatsapp/model/companion/CompanionSyncKey.java +++ b/src/main/java/it/auties/whatsapp/model/companion/CompanionSyncKey.java @@ -1,18 +1,19 @@ package it.auties.whatsapp.model.companion; +import it.auties.protobuf.annotation.ProtobufMessage; 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; +@ProtobufMessage 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 b0ba77f1b..de4e50749 100644 --- a/src/main/java/it/auties/whatsapp/model/contact/Contact.java +++ b/src/main/java/it/auties/whatsapp/model/contact/Contact.java @@ -1,8 +1,8 @@ package it.auties.whatsapp.model.contact; import com.fasterxml.jackson.annotation.JsonCreator; +import it.auties.protobuf.annotation.ProtobufMessage; 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; @@ -19,7 +19,8 @@ * 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, ProtobufMessage { +@ProtobufMessage +public final class Contact implements JidProvider { /** * The non-null unique jid used to identify this contact */ diff --git a/src/main/java/it/auties/whatsapp/model/contact/ContactCard.java b/src/main/java/it/auties/whatsapp/model/contact/ContactCard.java index 50581bfde..fce8cb7e7 100644 --- a/src/main/java/it/auties/whatsapp/model/contact/ContactCard.java +++ b/src/main/java/it/auties/whatsapp/model/contact/ContactCard.java @@ -5,21 +5,24 @@ import ezvcard.VCardVersion; import ezvcard.property.SimpleProperty; import ezvcard.property.Telephone; -import it.auties.protobuf.annotation.ProtobufConverter; +import it.auties.protobuf.annotation.ProtobufDeserializer; +import it.auties.protobuf.annotation.ProtobufSerializer; import it.auties.whatsapp.model.jid.Jid; import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; -import static it.auties.whatsapp.util.Specification.Whatsapp.*; - /** * A model class to represent and build the vcard of a contact */ public sealed interface ContactCard { - @ProtobufConverter + String BUSINESS_NAME_VCARD_PROPERTY = "X-WA-BIZ-NAME"; + String PHONE_NUMBER_VCARD_PROPERTY = "WAID"; + String DEFAULT_NUMBER_VCARD_TYPE = "CELL"; + + @ProtobufDeserializer static ContactCard ofNullable(String vcard) { return vcard == null ? null : of(vcard); } @@ -51,7 +54,7 @@ static ContactCard of(String vcard) { /** * Creates a new vcard * - * @param name the nullable name of the contact + * @param name the nullable name of the contact * @param phoneNumber the non-null phone number of the contact * @return a vcard */ @@ -62,8 +65,8 @@ static ContactCard of(String name, Jid phoneNumber) { /** * Creates a new vcard * - * @param name the nullable name of the contact - * @param phoneNumber the non-null phone number of the contact + * @param name the nullable name of the contact + * @param phoneNumber the non-null phone number of the contact * @param businessName the nullable business name of the contact * @return a vcard */ @@ -92,7 +95,7 @@ private static List joinPhoneNumbers(List first, List second) { return Stream.of(first, second).flatMap(Collection::stream).toList(); } - @ProtobufConverter + @ProtobufSerializer String toVcard(); /** @@ -137,7 +140,7 @@ public void addPhoneNumber(String category, Jid contact) { * @return a non-null String */ @Override - @ProtobufConverter + @ProtobufSerializer public String toVcard() { var vcard = new VCard(); vcard.setVersion(VCardVersion.valueOfByStr(version())); @@ -153,7 +156,7 @@ public String toVcard() { */ record Raw(String toVcard) implements ContactCard { @Override - @ProtobufConverter + @ProtobufSerializer public String toVcard() { return toVcard; } 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 caf4c3944..5f9b6f252 100644 --- a/src/main/java/it/auties/whatsapp/model/contact/ContactStatus.java +++ b/src/main/java/it/auties/whatsapp/model/contact/ContactStatus.java @@ -1,8 +1,8 @@ package it.auties.whatsapp.model.contact; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; import java.util.Arrays; import java.util.Optional; @@ -11,19 +11,23 @@ * The constants of this enumerated type describe the various status that a {@link Contact} can be * in */ -public enum ContactStatus implements ProtobufEnum { +@ProtobufEnum +public enum ContactStatus { /** * When the contact is online */ AVAILABLE(0), + /** * When the contact is offline */ UNAVAILABLE(1), + /** * When the contact is writing a text message */ COMPOSING(2), + /** * When the contact is recording an audio message */ diff --git a/src/main/java/it/auties/whatsapp/model/info/AdReplyInfo.java b/src/main/java/it/auties/whatsapp/model/info/AdReplyInfo.java index 6a312950b..b3f06a1cb 100644 --- a/src/main/java/it/auties/whatsapp/model/info/AdReplyInfo.java +++ b/src/main/java/it/auties/whatsapp/model/info/AdReplyInfo.java @@ -1,10 +1,9 @@ package it.auties.whatsapp.model.info; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.Optional; @@ -13,7 +12,7 @@ /** * A model class that holds the information related to an companion reply. */ -@ProtobufMessageName("ContextInfo.AdReplyInfo") +@ProtobufMessage(name = "ContextInfo.AdReplyInfo") public record AdReplyInfo( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String advertiserName, @@ -23,14 +22,14 @@ public record AdReplyInfo( Optional thumbnail, @ProtobufProperty(index = 17, type = ProtobufType.STRING) Optional caption -) implements Info, ProtobufMessage { +) implements Info { /** * The constants of this enumerated type describe the various types of companion that a * {@link AdReplyInfo} can link to */ - @ProtobufMessageName("ContextInfo.AdReplyInfo.MediaType") - public enum MediaType implements ProtobufEnum { + @ProtobufEnum(name = "ContextInfo.AdReplyInfo.MediaType") + public enum MediaType { /** * Unknown type */ diff --git a/src/main/java/it/auties/whatsapp/model/info/BusinessAccountLinkInfo.java b/src/main/java/it/auties/whatsapp/model/info/BusinessAccountLinkInfo.java index ce0a5710e..d5ee707ac 100644 --- a/src/main/java/it/auties/whatsapp/model/info/BusinessAccountLinkInfo.java +++ b/src/main/java/it/auties/whatsapp/model/info/BusinessAccountLinkInfo.java @@ -1,10 +1,9 @@ package it.auties.whatsapp.model.info; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.util.Clock; @@ -14,7 +13,7 @@ /** * A model class that holds a payload about a business link info. */ -@ProtobufMessageName("BizAccountLinkInfo") +@ProtobufMessage(name = "BizAccountLinkInfo") public record BusinessAccountLinkInfo( @ProtobufProperty(index = 1, type = ProtobufType.UINT64) long businessId, @@ -26,7 +25,7 @@ public record BusinessAccountLinkInfo( HostStorageType hostStorage, @ProtobufProperty(index = 5, type = ProtobufType.OBJECT) AccountType accountType -) implements ProtobufMessage { +) { /** * Returns this object's timestampSeconds * @@ -39,8 +38,8 @@ public Optional issueTime() { /** * The constants of this enumerated type describe the various types of business accounts */ - @ProtobufMessageName("BizAccountLinkInfo.AccountType") - public enum AccountType implements ProtobufEnum { + @ProtobufEnum(name = "BizAccountLinkInfo.AccountType") + public enum AccountType { /** * Enterprise */ @@ -61,8 +60,9 @@ public int index() { } } - @ProtobufMessageName("BizAccountLinkInfo.HostStorageType") - public enum HostStorageType implements ProtobufEnum { + @ProtobufEnum(name = "BizAccountLinkInfo.HostStorageType") + public enum HostStorageType { + /** * Hosted on a private server ("On-Premise") */ diff --git a/src/main/java/it/auties/whatsapp/model/info/BusinessIdentityInfo.java b/src/main/java/it/auties/whatsapp/model/info/BusinessIdentityInfo.java index 8684624c6..13c4be2da 100644 --- a/src/main/java/it/auties/whatsapp/model/info/BusinessIdentityInfo.java +++ b/src/main/java/it/auties/whatsapp/model/info/BusinessIdentityInfo.java @@ -1,10 +1,9 @@ package it.auties.whatsapp.model.info; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.business.BusinessVerifiedNameCertificate; import it.auties.whatsapp.util.Clock; @@ -16,7 +15,7 @@ /** * A model class that holds the information related to the identity of a business account. */ -@ProtobufMessageName("BizIdentityInfo") +@ProtobufMessage(name = "BizIdentityInfo") public record BusinessIdentityInfo( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) VerifiedLevel level, @@ -34,7 +33,7 @@ public record BusinessIdentityInfo( long privacyModeTimestampSeconds, @ProtobufProperty(index = 8, type = ProtobufType.UINT64) long featureControls -) implements Info, ProtobufMessage { +) implements Info { /** * Returns the privacy mode timestampSeconds * @@ -47,8 +46,8 @@ public Optional privacyModeTimestamp() { /** * The constants of this enumerated type describe the various types of actors of a business account */ - @ProtobufMessageName("BizIdentityInfo.ActualActorsType") - public enum ActorsType implements ProtobufEnum { + @ProtobufEnum(name = "BizIdentityInfo.ActualActorsType") + public enum ActorsType { /** * Self */ @@ -73,8 +72,8 @@ public int index() { * The constants of this enumerated type describe the various types of verification that a business * account can have */ - @ProtobufMessageName("BizIdentityInfo.VerifiedLevelValue") - public enum VerifiedLevel implements ProtobufEnum { + @ProtobufEnum(name = "BizIdentityInfo.VerifiedLevelValue") + public enum VerifiedLevel { /** * Unknown */ @@ -101,8 +100,8 @@ public int index() { } } - @ProtobufMessageName("BizIdentityInfo.HostStorageType") - public enum HostStorageType implements ProtobufEnum { + @ProtobufEnum(name = "BizIdentityInfo.HostStorageType") + public enum HostStorageType { /** * Hosted on a private server ("On-Premise") */ 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 c73ee00a9..a776dce55 100644 --- a/src/main/java/it/auties/whatsapp/model/info/ChatMessageInfo.java +++ b/src/main/java/it/auties/whatsapp/model/info/ChatMessageInfo.java @@ -2,11 +2,10 @@ import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonCreator; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.business.BusinessPrivacyStatus; import it.auties.whatsapp.model.chat.Chat; @@ -29,8 +28,8 @@ /** * A model class that holds the information related to a {@link Message}. */ -@ProtobufMessageName("WebMessageInfo") -public final class ChatMessageInfo implements MessageInfo, MessageStatusInfo, ProtobufMessage { +@ProtobufMessage(name = "WebMessageInfo") +public final class ChatMessageInfo implements MessageInfo, MessageStatusInfo { @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) private final ChatMessageKey key; @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) @@ -117,15 +116,15 @@ public final class ChatMessageInfo implements MessageInfo, MessageStatusInfo stubParameters, int duration, List labels, PaymentInfo paymentInfo, LiveLocationMessage finalLiveLocation, PaymentInfo quotedPaymentInfo, long ephemeralStartTimestamp, int ephemeralDuration, boolean enableEphemeral, boolean ephemeralOutOfSync, BusinessPrivacyStatus businessPrivacyStatus, String businessVerifiedName, MediaData mediaData, PhotoChange photoChange, MessageReceipt receipt, List reactions, MediaData quotedStickerData, byte[] futureProofData, PublicServiceAnnouncementStatus psaStatus, List pollUpdates, PollAdditionalMetadata pollAdditionalMetadata, String agentId, boolean statusAlreadyViewed, byte[] messageSecret, KeepInChat keepInChat, Jid originalSender, long revokeTimestampSeconds, Chat chat, Contact sender) { + public ChatMessageInfo(ChatMessageKey key, MessageContainer message, long timestampSeconds, MessageStatus status, Jid senderJid, long messageC2STimestamp, boolean ignore, boolean starred, boolean broadcast, String pushName, byte[] mediaCiphertextSha256, boolean multicast, boolean urlText, boolean urlNumber, StubType stubType, boolean clearMedia, List stubParameters, int duration, List labels, PaymentInfo paymentInfo, LiveLocationMessage finalLiveLocation, PaymentInfo quotedPaymentInfo, long ephemeralStartTimestamp, int ephemeralDuration, boolean enableEphemeral, boolean ephemeralOutOfSync, BusinessPrivacyStatus businessPrivacyStatus, String businessVerifiedName, MediaData mediaData, PhotoChange photoChange, MessageReceipt receipt, List reactions, MediaData quotedStickerData, byte[] futureProofData, PublicServiceAnnouncementStatus psaStatus, List pollUpdates, PollAdditionalMetadata pollAdditionalMetadata, String agentId, boolean statusAlreadyViewed, byte[] messageSecret, KeepInChat keepInChat, Jid originalSender, long revokeTimestampSeconds) { this.key = key; - this.message = message; + this.message = Objects.requireNonNullElseGet(message, MessageContainer::empty); this.timestampSeconds = timestampSeconds; this.status = status; this.senderJid = senderJid; @@ -154,7 +153,7 @@ public ChatMessageInfo(ChatMessageKey key, MessageContainer message, long timest this.businessVerifiedName = businessVerifiedName; this.mediaData = mediaData; this.photoChange = photoChange; - this.receipt = receipt; + this.receipt = Objects.requireNonNullElseGet(receipt, MessageReceipt::new); this.reactions = reactions; this.quotedStickerData = quotedStickerData; this.futureProofData = futureProofData; @@ -167,14 +166,12 @@ public ChatMessageInfo(ChatMessageKey key, MessageContainer message, long timest this.keepInChat = keepInChat; this.originalSender = originalSender; this.revokeTimestampSeconds = revokeTimestampSeconds; - this.chat = chat; - this.sender = sender; } - - public ChatMessageInfo(ChatMessageKey key, MessageContainer message, long timestampSeconds, MessageStatus status, Jid senderJid, long messageC2STimestamp, boolean ignore, boolean starred, boolean broadcast, String pushName, byte[] mediaCiphertextSha256, boolean multicast, boolean urlText, boolean urlNumber, StubType stubType, boolean clearMedia, List stubParameters, int duration, List labels, PaymentInfo paymentInfo, LiveLocationMessage finalLiveLocation, PaymentInfo quotedPaymentInfo, long ephemeralStartTimestamp, int ephemeralDuration, boolean enableEphemeral, boolean ephemeralOutOfSync, BusinessPrivacyStatus businessPrivacyStatus, String businessVerifiedName, MediaData mediaData, PhotoChange photoChange, MessageReceipt receipt, List reactions, MediaData quotedStickerData, byte[] futureProofData, PublicServiceAnnouncementStatus psaStatus, List pollUpdates, PollAdditionalMetadata pollAdditionalMetadata, String agentId, boolean statusAlreadyViewed, byte[] messageSecret, KeepInChat keepInChat, Jid originalSender, long revokeTimestampSeconds) { + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + public ChatMessageInfo(ChatMessageKey key, MessageContainer message, long timestampSeconds, MessageStatus status, Jid senderJid, long messageC2STimestamp, boolean ignore, boolean starred, boolean broadcast, String pushName, byte[] mediaCiphertextSha256, boolean multicast, boolean urlText, boolean urlNumber, StubType stubType, boolean clearMedia, List stubParameters, int duration, List labels, PaymentInfo paymentInfo, LiveLocationMessage finalLiveLocation, PaymentInfo quotedPaymentInfo, long ephemeralStartTimestamp, int ephemeralDuration, boolean enableEphemeral, boolean ephemeralOutOfSync, BusinessPrivacyStatus businessPrivacyStatus, String businessVerifiedName, MediaData mediaData, PhotoChange photoChange, MessageReceipt receipt, List reactions, MediaData quotedStickerData, byte[] futureProofData, PublicServiceAnnouncementStatus psaStatus, List pollUpdates, PollAdditionalMetadata pollAdditionalMetadata, String agentId, boolean statusAlreadyViewed, byte[] messageSecret, KeepInChat keepInChat, Jid originalSender, long revokeTimestampSeconds, Chat chat, Contact sender) { this.key = key; - this.message = Objects.requireNonNullElseGet(message, MessageContainer::empty); + this.message = message; this.timestampSeconds = timestampSeconds; this.status = status; this.senderJid = senderJid; @@ -203,7 +200,7 @@ public ChatMessageInfo(ChatMessageKey key, MessageContainer message, long timest this.businessVerifiedName = businessVerifiedName; this.mediaData = mediaData; this.photoChange = photoChange; - this.receipt = Objects.requireNonNullElseGet(receipt, MessageReceipt::new); + this.receipt = receipt; this.reactions = reactions; this.quotedStickerData = quotedStickerData; this.futureProofData = futureProofData; @@ -216,6 +213,8 @@ public ChatMessageInfo(ChatMessageKey key, MessageContainer message, long timest this.keepInChat = keepInChat; this.originalSender = originalSender; this.revokeTimestampSeconds = revokeTimestampSeconds; + this.chat = chat; + this.sender = sender; } /** @@ -546,10 +545,12 @@ public ChatMessageInfo setRevokeTimestampSeconds(long revokeTimestampSeconds) { return this; } + /** * The constants of this enumerated type describe the various types of server message that a {@link ChatMessageInfo} can describe */ - public enum StubType implements ProtobufEnum { + @ProtobufEnum + public enum StubType { UNKNOWN(0, List.of("unknown")), REVOKE(1, List.of("revoked")), CIPHERTEXT(2, List.of("ciphertext")), 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 09f194377..29c48bf05 100644 --- a/src/main/java/it/auties/whatsapp/model/info/ContextInfo.java +++ b/src/main/java/it/auties/whatsapp/model/info/ContextInfo.java @@ -2,9 +2,8 @@ import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonCreator; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.button.base.ButtonActionLink; import it.auties.whatsapp.model.chat.Chat; @@ -21,8 +20,8 @@ /** * A model class that holds the information related to a {@link it.auties.whatsapp.model.message.model.ContextualMessage}. */ -@ProtobufMessageName("ContextInfo") -public final class ContextInfo implements Info, ProtobufMessage { +@ProtobufMessage(name = "ContextInfo") +public final class ContextInfo implements Info { /** * The jid of the message that this ContextualMessage quotes */ @@ -101,7 +100,6 @@ public final class ContextInfo implements Info, ProtobufMessage { */ @ProtobufProperty(index = 25, type = ProtobufType.UINT32) private int ephemeralExpiration; - /** * The timestampSeconds, that is the seconds in seconds since {@link java.time.Instant#EPOCH}, of the * last modification to the ephemeral settings for the chat where this ContextualMessage was diff --git a/src/main/java/it/auties/whatsapp/model/info/DeviceContextInfo.java b/src/main/java/it/auties/whatsapp/model/info/DeviceContextInfo.java index da626a369..82a7d0456 100644 --- a/src/main/java/it/auties/whatsapp/model/info/DeviceContextInfo.java +++ b/src/main/java/it/auties/whatsapp/model/info/DeviceContextInfo.java @@ -1,27 +1,24 @@ package it.auties.whatsapp.model.info; import com.fasterxml.jackson.annotation.JsonCreator; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.sync.DeviceListMetadata; import java.util.Optional; -@ProtobufMessageName("MessageContextInfo") -public final class DeviceContextInfo implements Info, ProtobufMessage { +@ProtobufMessage(name = "MessageContextInfo") +public final class DeviceContextInfo implements Info { @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) private final DeviceListMetadata deviceListMetadata; @ProtobufProperty(index = 2, type = ProtobufType.INT32) private final int deviceListMetadataVersion; - - @ProtobufProperty(index = 3, type = ProtobufType.BYTES) - private byte[] messageSecret; - @ProtobufProperty(index = 4, type = ProtobufType.BYTES) private final byte[] paddingBytes; + @ProtobufProperty(index = 3, type = ProtobufType.BYTES) + private byte[] messageSecret; @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) public DeviceContextInfo(DeviceListMetadata deviceListMetadata, int deviceListMetadataVersion, byte[] messageSecret, byte[] paddingBytes) { diff --git a/src/main/java/it/auties/whatsapp/model/info/ExternalAdReplyInfo.java b/src/main/java/it/auties/whatsapp/model/info/ExternalAdReplyInfo.java index 3fa195758..8fb90fe12 100644 --- a/src/main/java/it/auties/whatsapp/model/info/ExternalAdReplyInfo.java +++ b/src/main/java/it/auties/whatsapp/model/info/ExternalAdReplyInfo.java @@ -1,10 +1,9 @@ package it.auties.whatsapp.model.info; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.Optional; @@ -13,7 +12,7 @@ /** * A model class that holds the information related to an advertisement. */ -@ProtobufMessageName("ContextInfo.ExternalAdReplyInfo") +@ProtobufMessage(name = "ContextInfo.ExternalAdReplyInfo") public record ExternalAdReplyInfo( @ProtobufProperty(index = 1, type = ProtobufType.STRING) Optional title, @@ -41,20 +40,22 @@ public record ExternalAdReplyInfo( boolean showAdAttribution, @ProtobufProperty(index = 13, type = ProtobufType.STRING) Optional ctwaClid -) implements Info, ProtobufMessage { +) implements Info { /** * The constants of this enumerated type describe the various types of media that an ad can wrap */ - @ProtobufMessageName("ChatRowOpaqueData.DraftMessage.CtwaContextData.ContextInfoExternalAdReplyInfoMediaType") - public enum MediaType implements ProtobufEnum { + @ProtobufEnum(name = "ChatRowOpaqueData.DraftMessage.CtwaContextData.ContextInfoExternalAdReplyInfoMediaType") + public enum MediaType { /** * No media */ NONE(0), + /** * Image */ IMAGE(1), + /** * Video */ diff --git a/src/main/java/it/auties/whatsapp/model/info/NativeFlowInfo.java b/src/main/java/it/auties/whatsapp/model/info/NativeFlowInfo.java index 2671512dd..14d48e7f2 100644 --- a/src/main/java/it/auties/whatsapp/model/info/NativeFlowInfo.java +++ b/src/main/java/it/auties/whatsapp/model/info/NativeFlowInfo.java @@ -1,21 +1,20 @@ package it.auties.whatsapp.model.info; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.button.base.ButtonBody; /** * A model class that holds the information related to a native flow. */ -@ProtobufMessageName("Message.ButtonsMessage.Button.NativeFlowInfo") +@ProtobufMessage(name = "Message.ButtonsMessage.Button.NativeFlowInfo") public record NativeFlowInfo( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String name, @ProtobufProperty(index = 2, type = ProtobufType.STRING) String parameters -) implements Info, ButtonBody, ProtobufMessage { +) implements Info, ButtonBody { @Override public Type bodyType() { return Type.NATIVE_FLOW; 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 ef1a9a0d2..707a62e2c 100644 --- a/src/main/java/it/auties/whatsapp/model/info/NewsletterMessageInfo.java +++ b/src/main/java/it/auties/whatsapp/model/info/NewsletterMessageInfo.java @@ -2,8 +2,8 @@ import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonCreator; +import it.auties.protobuf.annotation.ProtobufMessage; 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; @@ -15,9 +15,8 @@ import java.time.ZonedDateTime; import java.util.*; -public final class NewsletterMessageInfo implements MessageInfo, MessageStatusInfo, ProtobufMessage { - @JsonBackReference - private Newsletter newsletter; +@ProtobufMessage +public final class NewsletterMessageInfo implements MessageInfo, MessageStatusInfo { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private final String id; @ProtobufProperty(index = 2, type = ProtobufType.INT32) @@ -26,10 +25,12 @@ public final class NewsletterMessageInfo implements MessageInfo, MessageStatusIn private final Long timestampSeconds; @ProtobufProperty(index = 4, type = ProtobufType.UINT64) private final Long views; - @ProtobufProperty(index = 5, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.OBJECT) + @ProtobufProperty(index = 5, type = ProtobufType.MAP, mapKeyType = ProtobufType.STRING, mapValueType = ProtobufType.OBJECT) final Map reactions; @ProtobufProperty(index = 6, type = ProtobufType.OBJECT) private final MessageContainer message; + @JsonBackReference + private Newsletter newsletter; @ProtobufProperty(index = 7, type = ProtobufType.OBJECT) private MessageStatus status; @@ -131,7 +132,7 @@ public void incrementReaction(String code, boolean fromMe) { public void decrementReaction(String code) { findReaction(code).ifPresent(reaction -> { - if(reaction.count() <= 1) { + if (reaction.count() <= 1) { removeReaction(reaction.content()); return; } diff --git a/src/main/java/it/auties/whatsapp/model/info/NotificationMessageInfo.java b/src/main/java/it/auties/whatsapp/model/info/NotificationMessageInfo.java index fd6fbaccd..89689bcc3 100644 --- a/src/main/java/it/auties/whatsapp/model/info/NotificationMessageInfo.java +++ b/src/main/java/it/auties/whatsapp/model/info/NotificationMessageInfo.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.info; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.message.model.ChatMessageKey; import it.auties.whatsapp.model.message.model.MessageContainer; @@ -11,7 +10,7 @@ import java.time.ZonedDateTime; import java.util.Optional; -@ProtobufMessageName("NotificationMessageInfo") +@ProtobufMessage(name = "NotificationMessageInfo") public record NotificationMessageInfo( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) ChatMessageKey key, @@ -21,7 +20,7 @@ public record NotificationMessageInfo( long messageTimestampSeconds, @ProtobufProperty(index = 4, type = ProtobufType.STRING) Optional participant -) implements Info, ProtobufMessage { +) implements Info { /** * Returns when the message was sent * diff --git a/src/main/java/it/auties/whatsapp/model/info/PaymentInfo.java b/src/main/java/it/auties/whatsapp/model/info/PaymentInfo.java index c1dd58495..b471b4d2d 100644 --- a/src/main/java/it/auties/whatsapp/model/info/PaymentInfo.java +++ b/src/main/java/it/auties/whatsapp/model/info/PaymentInfo.java @@ -1,10 +1,9 @@ package it.auties.whatsapp.model.info; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -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.ChatMessageKey; @@ -18,7 +17,7 @@ /** * A model class that holds the information related to a payment. */ -@ProtobufMessageName("PaymentInfo") +@ProtobufMessage(name = "PaymentInfo") public record PaymentInfo( @Deprecated @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) @@ -47,7 +46,7 @@ public record PaymentInfo( PaymentMoney primaryAmount, @ProtobufProperty(index = 13, type = ProtobufType.OBJECT) PaymentMoney exchangeAmount -) implements Info, ProtobufMessage { +) implements Info { /** * Returns when the transaction happened * @@ -70,8 +69,8 @@ public Optional expiryTimestamp() { * The constants of this enumerated type describe the status of a payment described by a * {@link PaymentInfo} */ - @ProtobufMessageName("PaymentInfo.Status") - public enum Status implements ProtobufEnum { + @ProtobufEnum(name = "PaymentInfo.Status") + public enum Status { /** * Unknown status */ @@ -136,8 +135,8 @@ public int index() { * The constants of this enumerated type describe the currencies supported for a transaction * described by a {@link PaymentInfo} */ - @ProtobufMessageName("PaymentInfo.Currency") - public enum Currency implements ProtobufEnum { + @ProtobufEnum(name = "PaymentInfo.Currency") + public enum Currency { /** * Unknown currency */ @@ -158,8 +157,8 @@ public int index() { } } - @ProtobufMessageName("PaymentInfo.TxnStatus") - public enum TransactionStatus implements ProtobufEnum { + @ProtobufEnum(name = "PaymentInfo.TxnStatus") + public enum TransactionStatus { UNKNOWN(0), PENDING_SETUP(1), PENDING_RECEIVER_SETUP(2), 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 a2aff998d..2811ab79e 100644 --- a/src/main/java/it/auties/whatsapp/model/info/ProductListInfo.java +++ b/src/main/java/it/auties/whatsapp/model/info/ProductListInfo.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.info; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; 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.product.ProductListHeaderImage; @@ -13,7 +12,7 @@ /** * A model class that holds the information related to a list of products. */ -@ProtobufMessageName("Message.ListMessage.ProductListInfo") +@ProtobufMessage(name = "Message.ListMessage.ProductListInfo") public record ProductListInfo( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) List productSections, @@ -21,6 +20,6 @@ public record ProductListInfo( ProductListHeaderImage headerImage, @ProtobufProperty(index = 3, type = ProtobufType.STRING) Jid seller -) implements Info, ProtobufMessage { +) implements Info { } 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 8201b6ef4..09340f56c 100644 --- a/src/main/java/it/auties/whatsapp/model/info/WebNotificationsInfo.java +++ b/src/main/java/it/auties/whatsapp/model/info/WebNotificationsInfo.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.info; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.util.Clock; @@ -10,7 +9,7 @@ import java.util.List; import java.util.Optional; -@ProtobufMessageName("WebNotificationsInfo") +@ProtobufMessage(name = "WebNotificationsInfo") public record WebNotificationsInfo( @ProtobufProperty(index = 2, type = ProtobufType.UINT64) long timestampSeconds, @@ -20,7 +19,7 @@ public record WebNotificationsInfo( int notifyMessageCount, @ProtobufProperty(index = 5, type = ProtobufType.OBJECT) List notifyMessages -) implements Info, ProtobufMessage { +) implements Info { /** * Returns when the notification was sent * diff --git a/src/main/java/it/auties/whatsapp/model/jid/Jid.java b/src/main/java/it/auties/whatsapp/model/jid/Jid.java index 390668a70..2dbbd7f55 100644 --- a/src/main/java/it/auties/whatsapp/model/jid/Jid.java +++ b/src/main/java/it/auties/whatsapp/model/jid/Jid.java @@ -2,7 +2,8 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; -import it.auties.protobuf.annotation.ProtobufConverter; +import it.auties.protobuf.annotation.ProtobufDeserializer; +import it.auties.protobuf.annotation.ProtobufSerializer; import it.auties.whatsapp.model.signal.session.SessionAddress; import java.util.Objects; @@ -32,7 +33,7 @@ public static Jid ofServer(JidServer server) { return of(null, server); } - @ProtobufConverter // Reserved for protobuf + @ProtobufDeserializer // Reserved for protobuf public static Jid ofProtobuf(String input) { return input == null ? null : Jid.of(input); } @@ -189,6 +190,26 @@ public Jid withServer(JidServer server) { return new Jid(user(), server, device, agent); } + /** + * Returns a new jid using with a different agent + * + * @param agent the new agent + * @return a non-null jid + */ + public Jid withAgent(Integer agent) { + return new Jid(user(), server, device, agent); + } + + /** + * Returns a new jid using with a different device + * + * @param device the new device + * @return a non-null jid + */ + public Jid withDevice(Integer device) { + return new Jid(user(), server, device, agent); + } + /** * Converts this jid to a user jid * @@ -213,7 +234,7 @@ public String toPhoneNumber() { * @return a non-null String */ @JsonValue - @ProtobufConverter + @ProtobufSerializer @Override public String toString() { var user = Objects.requireNonNullElse(user(), ""); @@ -253,7 +274,7 @@ public Integer device() { * @return a boolean */ public boolean hasDevice() { - return device != null; + return device != null && device != 0; } @Override @@ -267,7 +288,7 @@ public Integer agent() { * @return a boolean */ public boolean hasAgent() { - return agent != null; + return agent != null && agent != 0; } @Override diff --git a/src/main/java/it/auties/whatsapp/model/media/MediaData.java b/src/main/java/it/auties/whatsapp/model/media/MediaData.java index b8aa3e616..31a452933 100644 --- a/src/main/java/it/auties/whatsapp/model/media/MediaData.java +++ b/src/main/java/it/auties/whatsapp/model/media/MediaData.java @@ -1,15 +1,14 @@ package it.auties.whatsapp.model.media; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("MediaData") +@ProtobufMessage(name = "MediaData") public record MediaData( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String localPath -) implements ProtobufMessage { +) { } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/media/MediaKeys.java b/src/main/java/it/auties/whatsapp/model/media/MediaKeys.java index 477c1bedb..c6c99bdf5 100644 --- a/src/main/java/it/auties/whatsapp/model/media/MediaKeys.java +++ b/src/main/java/it/auties/whatsapp/model/media/MediaKeys.java @@ -6,8 +6,8 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; -import static it.auties.whatsapp.util.Specification.Signal.IV_LENGTH; -import static it.auties.whatsapp.util.Specification.Signal.KEY_LENGTH; +import static it.auties.whatsapp.util.SignalConstants.IV_LENGTH; +import static it.auties.whatsapp.util.SignalConstants.KEY_LENGTH; public record MediaKeys(byte[] mediaKey, byte[] iv, byte[] cipherKey, byte[] macKey, byte[] ref) { private static final int EXPANDED_SIZE = 112; diff --git a/src/main/java/it/auties/whatsapp/model/media/MediaVisibility.java b/src/main/java/it/auties/whatsapp/model/media/MediaVisibility.java index 1f81152c8..5b6421149 100644 --- a/src/main/java/it/auties/whatsapp/model/media/MediaVisibility.java +++ b/src/main/java/it/auties/whatsapp/model/media/MediaVisibility.java @@ -1,15 +1,15 @@ package it.auties.whatsapp.model.media; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; -import it.auties.protobuf.model.ProtobufEnum; +import it.auties.protobuf.annotation.ProtobufMessage; /** * The constants of this enumerated type describe the various types of media visibility that can be * set for a chat */ -@ProtobufMessageName("MediaVisibility") -public enum MediaVisibility implements ProtobufEnum { +@ProtobufEnum(name = "MediaVisibility") +public enum MediaVisibility { /** * Default */ diff --git a/src/main/java/it/auties/whatsapp/model/media/MutableAttachmentProvider.java b/src/main/java/it/auties/whatsapp/model/media/MutableAttachmentProvider.java index c123b1903..babf5f428 100644 --- a/src/main/java/it/auties/whatsapp/model/media/MutableAttachmentProvider.java +++ b/src/main/java/it/auties/whatsapp/model/media/MutableAttachmentProvider.java @@ -1,6 +1,5 @@ package it.auties.whatsapp.model.media; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.whatsapp.model.message.model.MediaMessage; import it.auties.whatsapp.model.sync.ExternalBlobReference; import it.auties.whatsapp.model.sync.HistorySyncNotification; @@ -11,7 +10,7 @@ /** * A sealed interface that represents a class that can provide data about a media */ -public sealed interface MutableAttachmentProvider> extends ProtobufMessage +public sealed interface MutableAttachmentProvider> permits MediaMessage, ExternalBlobReference, HistorySyncNotification { /** * Returns the url to the media 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 15a64a093..79d177688 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 @@ -1,7 +1,7 @@ package it.auties.whatsapp.model.message.button; import it.auties.protobuf.annotation.ProtobufBuilder; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.button.base.Button; @@ -21,7 +21,7 @@ /** * A model class that represents a message that contains buttons inside */ -@ProtobufMessageName("Message.ButtonsMessage") +@ProtobufMessage(name = "Message.ButtonsMessage") public final class ButtonsMessage implements ButtonMessage, ContextualMessage { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private final ButtonsMessageHeaderText headerText; @@ -102,7 +102,7 @@ public Optional header() { return Optional.of(headerText); } - if (headerDocument != null) { + if (headerDocument != null) { return Optional.of(headerDocument); } @@ -116,31 +116,31 @@ public Optional header() { return Optional.ofNullable(headerLocation); } - + public Optional headerText() { return Optional.ofNullable(headerText); } - + public Optional headerDocument() { return Optional.ofNullable(headerDocument); } - + public Optional headerImage() { return Optional.ofNullable(headerImage); } - + public Optional headerVideo() { return Optional.ofNullable(headerVideo); } - + public Optional headerLocation() { return Optional.ofNullable(headerLocation); } - + public Optional body() { return Optional.ofNullable(body); } - + public Optional footer() { return Optional.ofNullable(footer); } @@ -163,7 +163,7 @@ public ButtonsMessage setContextInfo(ContextInfo contextInfo) { this.contextInfo = contextInfo; return this; } - + @Override public String toString() { return "ButtonsMessage[" + diff --git a/src/main/java/it/auties/whatsapp/model/message/button/ButtonsMessageHeader.java b/src/main/java/it/auties/whatsapp/model/message/button/ButtonsMessageHeader.java index 6862b1a22..118d460ad 100644 --- a/src/main/java/it/auties/whatsapp/model/message/button/ButtonsMessageHeader.java +++ b/src/main/java/it/auties/whatsapp/model/message/button/ButtonsMessageHeader.java @@ -1,7 +1,7 @@ package it.auties.whatsapp.model.message.button; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; import it.auties.whatsapp.model.message.standard.DocumentMessage; import it.auties.whatsapp.model.message.standard.ImageMessage; import it.auties.whatsapp.model.message.standard.LocationMessage; @@ -16,7 +16,8 @@ public sealed interface ButtonsMessageHeader permits ButtonsMessageHeaderText, D /** * The constants of this enumerated type describe the various types of headers that a {@link ButtonsMessage} can have */ - enum Type implements ProtobufEnum { + @ProtobufEnum + enum Type { /** * Unknown */ diff --git a/src/main/java/it/auties/whatsapp/model/message/button/ButtonsMessageHeaderText.java b/src/main/java/it/auties/whatsapp/model/message/button/ButtonsMessageHeaderText.java index 75ddeb61f..c7c831a49 100644 --- a/src/main/java/it/auties/whatsapp/model/message/button/ButtonsMessageHeaderText.java +++ b/src/main/java/it/auties/whatsapp/model/message/button/ButtonsMessageHeaderText.java @@ -1,14 +1,15 @@ package it.auties.whatsapp.model.message.button; -import it.auties.protobuf.annotation.ProtobufConverter; +import it.auties.protobuf.annotation.ProtobufDeserializer; +import it.auties.protobuf.annotation.ProtobufSerializer; public record ButtonsMessageHeaderText(String text) implements ButtonsMessageHeader { - @ProtobufConverter + @ProtobufDeserializer public static ButtonsMessageHeaderText of(String text) { return new ButtonsMessageHeaderText(text); } - @ProtobufConverter + @ProtobufSerializer public String text() { return text; } diff --git a/src/main/java/it/auties/whatsapp/model/message/button/ButtonsResponseMessage.java b/src/main/java/it/auties/whatsapp/model/message/button/ButtonsResponseMessage.java index 597f9f3ac..e0aa6e820 100644 --- a/src/main/java/it/auties/whatsapp/model/message/button/ButtonsResponseMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/button/ButtonsResponseMessage.java @@ -1,15 +1,16 @@ package it.auties.whatsapp.model.message.button; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.button.base.Button; import it.auties.whatsapp.model.button.base.ButtonBody; import it.auties.whatsapp.model.button.base.ButtonText; import it.auties.whatsapp.model.info.ChatMessageInfo; import it.auties.whatsapp.model.info.ContextInfo; +import it.auties.whatsapp.model.message.button.ButtonsResponseMessage.ResponseType; import it.auties.whatsapp.model.message.model.ButtonReplyMessage; import it.auties.whatsapp.model.message.model.MessageType; @@ -19,7 +20,7 @@ * A model class that represents a message that contains a newsletters to a previous * {@link ButtonsMessage} */ -@ProtobufMessageName("Message.ButtonsResponseMessage") +@ProtobufMessage(name = "Message.ButtonsResponseMessage") public final class ButtonsResponseMessage implements ButtonReplyMessage { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private final String buttonId; @@ -84,7 +85,8 @@ public String toString() { } - public enum ResponseType implements ProtobufEnum { + @ProtobufEnum + public enum ResponseType { UNKNOWN(0), SELECTED_DISPLAY_TEXT(1); diff --git a/src/main/java/it/auties/whatsapp/model/message/button/InteractiveMessage.java b/src/main/java/it/auties/whatsapp/model/message/button/InteractiveMessage.java index 8cccf85d2..ce6823815 100644 --- a/src/main/java/it/auties/whatsapp/model/message/button/InteractiveMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/button/InteractiveMessage.java @@ -1,7 +1,7 @@ package it.auties.whatsapp.model.message.button; import it.auties.protobuf.annotation.ProtobufBuilder; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.button.interactive.*; @@ -18,7 +18,7 @@ * A model class that represents a message holding an interactive message inside. Not really clear * how this could be used, contributions are welcomed. */ -@ProtobufMessageName("Message.InteractiveMessage") +@ProtobufMessage(name = "Message.InteractiveMessage") public final class InteractiveMessage implements ContextualMessage, ButtonMessage, TemplateFormatter { @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) private final InteractiveHeader header; @@ -56,7 +56,8 @@ static InteractiveMessage simpleBuilder(InteractiveHeader header, String body, S case InteractiveShop interactiveShop -> builder.contentShop(interactiveShop); case InteractiveCollection interactiveCollection -> builder.contentCollection(interactiveCollection); case InteractiveNativeFlow interactiveNativeFlow -> builder.contentNativeFlow(interactiveNativeFlow); - case null -> {} + case null -> { + } } return builder.build(); } diff --git a/src/main/java/it/auties/whatsapp/model/message/button/InteractiveMessageContent.java b/src/main/java/it/auties/whatsapp/model/message/button/InteractiveMessageContent.java index f6024eae5..99a7ea847 100644 --- a/src/main/java/it/auties/whatsapp/model/message/button/InteractiveMessageContent.java +++ b/src/main/java/it/auties/whatsapp/model/message/button/InteractiveMessageContent.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.message.button; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.whatsapp.model.button.interactive.InteractiveCollection; import it.auties.whatsapp.model.button.interactive.InteractiveNativeFlow; import it.auties.whatsapp.model.button.interactive.InteractiveShop; @@ -10,7 +9,7 @@ /** * A model class that represents a message that can be used as the content of a {@link it.auties.whatsapp.model.message.button.InteractiveMessage} */ -public sealed interface InteractiveMessageContent extends ProtobufMessage permits InteractiveShop, InteractiveCollection, InteractiveNativeFlow { +public sealed interface InteractiveMessageContent permits InteractiveShop, InteractiveCollection, InteractiveNativeFlow { /** * Returns the type of this content * @@ -22,7 +21,8 @@ public sealed interface InteractiveMessageContent extends ProtobufMessage permit * The constants of this enumerated type describe the various types of content that an interactive * message can wrap */ - enum Type implements ProtobufEnum { + @ProtobufEnum + enum Type { /** * No content */ diff --git a/src/main/java/it/auties/whatsapp/model/message/button/InteractiveResponseMessage.java b/src/main/java/it/auties/whatsapp/model/message/button/InteractiveResponseMessage.java index c77b3a4f1..8c00b4bc5 100644 --- a/src/main/java/it/auties/whatsapp/model/message/button/InteractiveResponseMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/button/InteractiveResponseMessage.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.message.button; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.button.interactive.InteractiveBody; @@ -11,7 +11,7 @@ import java.util.Optional; -@ProtobufMessageName("Message.InteractiveResponseMessage") +@ProtobufMessage(name = "Message.InteractiveResponseMessage") public final class InteractiveResponseMessage implements ContextualMessage { @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) private final InteractiveBody body; diff --git a/src/main/java/it/auties/whatsapp/model/message/button/ListMessage.java b/src/main/java/it/auties/whatsapp/model/message/button/ListMessage.java index 37650da8f..90ce96ede 100644 --- a/src/main/java/it/auties/whatsapp/model/message/button/ListMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/button/ListMessage.java @@ -1,11 +1,11 @@ package it.auties.whatsapp.model.message.button; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; import it.auties.protobuf.model.ProtobufType; -import it.auties.whatsapp.model.button.misc.ButtonSection; +import it.auties.whatsapp.model.button.base.ButtonSection; import it.auties.whatsapp.model.info.ContextInfo; import it.auties.whatsapp.model.info.ProductListInfo; import it.auties.whatsapp.model.message.model.ButtonMessage; @@ -18,7 +18,7 @@ /** * A model class that represents a message that contains a list of buttons or a list of products */ -@ProtobufMessageName("Message.ListMessage") +@ProtobufMessage(name = "Message.ListMessage") public final class ListMessage implements ContextualMessage, ButtonMessage { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private final String title; @@ -109,8 +109,8 @@ public String toString() { /** * The constants of this enumerated type describe the various types of {@link ListMessage} */ - @ProtobufMessageName("Message.ListMessage.Type") - public enum Type implements ProtobufEnum { + @ProtobufEnum(name = "Message.ListMessage.Type") + public enum Type { /** * Unknown */ diff --git a/src/main/java/it/auties/whatsapp/model/message/button/ListResponseMessage.java b/src/main/java/it/auties/whatsapp/model/message/button/ListResponseMessage.java index e64542f40..50369a3e3 100644 --- a/src/main/java/it/auties/whatsapp/model/message/button/ListResponseMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/button/ListResponseMessage.java @@ -1,11 +1,11 @@ package it.auties.whatsapp.model.message.button; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; import it.auties.protobuf.model.ProtobufType; -import it.auties.whatsapp.model.button.misc.SingleSelectReplyButton; +import it.auties.whatsapp.model.button.base.SingleSelectReplyButton; import it.auties.whatsapp.model.info.ContextInfo; import it.auties.whatsapp.model.message.model.ButtonReplyMessage; import it.auties.whatsapp.model.message.model.MessageType; @@ -16,25 +16,25 @@ * A model class that represents a message that contains a newsletters to a previous * {@link ListMessage} */ -@ProtobufMessageName("Message.ListResponseMessage") +@ProtobufMessage(name = "Message.ListResponseMessage") public final class ListResponseMessage implements ButtonReplyMessage { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private final String title; + @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) + private final Type listType; @ProtobufProperty(index = 3, type = ProtobufType.OBJECT) private final SingleSelectReplyButton reply; @ProtobufProperty(index = 4, type = ProtobufType.OBJECT) private ContextInfo contextInfo; @ProtobufProperty(index = 5, type = ProtobufType.STRING) private final String description; - @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) - private final Type listType; - public ListResponseMessage(String title, SingleSelectReplyButton reply, ContextInfo contextInfo, String description, Type listType) { + public ListResponseMessage(String title, Type listType, SingleSelectReplyButton reply, ContextInfo contextInfo, String description) { this.title = title; + this.listType = listType; this.reply = reply; this.contextInfo = contextInfo; this.description = description; - this.listType = listType; } @Override @@ -83,8 +83,8 @@ public String toString() { /** * The constants of this enumerated type describe the various types of {@link ListMessage} */ - @ProtobufMessageName("Message.ListResponseMessage.Type") - public enum Type implements ProtobufEnum { + @ProtobufEnum(name = "Message.ListResponseMessage.Type") + public enum Type { /** * Unknown */ diff --git a/src/main/java/it/auties/whatsapp/model/message/button/NativeFlowResponseMessage.java b/src/main/java/it/auties/whatsapp/model/message/button/NativeFlowResponseMessage.java index 3d3f246ff..5591780c9 100644 --- a/src/main/java/it/auties/whatsapp/model/message/button/NativeFlowResponseMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/button/NativeFlowResponseMessage.java @@ -1,12 +1,12 @@ package it.auties.whatsapp.model.message.button; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.message.model.ButtonMessage; import it.auties.whatsapp.model.message.model.MessageType; -@ProtobufMessageName("Message.InteractiveResponseMessage.NativeFlowResponseMessage") +@ProtobufMessage(name = "Message.InteractiveResponseMessage.NativeFlowResponseMessage") public record NativeFlowResponseMessage( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String name, diff --git a/src/main/java/it/auties/whatsapp/model/message/button/TemplateMessage.java b/src/main/java/it/auties/whatsapp/model/message/button/TemplateMessage.java index e9ba7fffa..64511aa19 100644 --- a/src/main/java/it/auties/whatsapp/model/message/button/TemplateMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/button/TemplateMessage.java @@ -1,7 +1,7 @@ package it.auties.whatsapp.model.message.button; import it.auties.protobuf.annotation.ProtobufBuilder; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.button.template.TemplateFormatter; @@ -21,7 +21,7 @@ * A model class that represents a message sent in a WhatsappBusiness chat that provides a list of * buttons to choose from. */ -@ProtobufMessageName("Message.TemplateMessage") +@ProtobufMessage(name = "Message.TemplateMessage") public final class TemplateMessage implements ContextualMessage, ButtonMessage { @ProtobufProperty(index = 9, type = ProtobufType.STRING) private final String id; diff --git a/src/main/java/it/auties/whatsapp/model/message/button/TemplateReplyMessage.java b/src/main/java/it/auties/whatsapp/model/message/button/TemplateReplyMessage.java index ca1450bab..ff99783a0 100644 --- a/src/main/java/it/auties/whatsapp/model/message/button/TemplateReplyMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/button/TemplateReplyMessage.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.message.button; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.button.template.highlyStructured.HighlyStructuredMessage; @@ -15,7 +15,7 @@ * A model class that represents a message that contains a newsletters to a previous * {@link HighlyStructuredMessage} */ -@ProtobufMessageName("Message.TemplateButtonReplyMessage") +@ProtobufMessage(name = "Message.TemplateButtonReplyMessage") public final class TemplateReplyMessage implements ButtonReplyMessage { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private final String id; diff --git a/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java b/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java index 85bb5334b..e1fa2322c 100644 --- a/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java +++ b/src/main/java/it/auties/whatsapp/model/message/model/ChatMessageKey.java @@ -1,9 +1,8 @@ package it.auties.whatsapp.model.message.model; import com.fasterxml.jackson.annotation.JsonCreator; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.info.ChatMessageInfo; import it.auties.whatsapp.model.jid.Jid; @@ -18,17 +17,14 @@ * A container for unique identifiers and metadata linked to a {@link Message} and contained in * {@link ChatMessageInfo}. */ -@ProtobufMessageName("MessageKey") -public final class ChatMessageKey implements ProtobufMessage { +@ProtobufMessage(name = "MessageKey") +public final class ChatMessageKey { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private Jid chatJid; - @ProtobufProperty(index = 2, type = ProtobufType.BOOL) private final boolean fromMe; - @ProtobufProperty(index = 3, type = ProtobufType.STRING) private final String id; - @ProtobufProperty(index = 4, type = ProtobufType.STRING) private Jid senderJid; diff --git a/src/main/java/it/auties/whatsapp/model/message/model/ContextualMessage.java b/src/main/java/it/auties/whatsapp/model/message/model/ContextualMessage.java index 42a96538e..94f8fe8ce 100644 --- a/src/main/java/it/auties/whatsapp/model/message/model/ContextualMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/model/ContextualMessage.java @@ -17,5 +17,6 @@ public sealed interface ContextualMessage> extend TemplateMessage, ButtonReplyMessage, MediaMessage, PaymentOrderMessage, ContactMessage, ContactsMessage, GroupInviteMessage, LiveLocationMessage, LocationMessage, PollCreationMessage, ProductMessage, RequestPhoneNumberMessage, TextMessage { Optional contextInfo(); + T setContextInfo(ContextInfo contextInfo); } diff --git a/src/main/java/it/auties/whatsapp/model/message/model/FutureMessageContainer.java b/src/main/java/it/auties/whatsapp/model/message/model/FutureMessageContainer.java index 580933705..4950146cf 100644 --- a/src/main/java/it/auties/whatsapp/model/message/model/FutureMessageContainer.java +++ b/src/main/java/it/auties/whatsapp/model/message/model/FutureMessageContainer.java @@ -1,18 +1,17 @@ package it.auties.whatsapp.model.message.model; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; /** * A container for a future message */ -@ProtobufMessageName("Message.FutureProofMessage") +@ProtobufMessage(name = "Message.FutureProofMessage") public record FutureMessageContainer( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) MessageContainer content -) implements ProtobufMessage { +) { static FutureMessageContainer of(Message message) { return new FutureMessageContainer(MessageContainer.of(message)); } diff --git a/src/main/java/it/auties/whatsapp/model/message/model/KeepInChat.java b/src/main/java/it/auties/whatsapp/model/message/model/KeepInChat.java index 4e3a9dc57..694e6967e 100644 --- a/src/main/java/it/auties/whatsapp/model/message/model/KeepInChat.java +++ b/src/main/java/it/auties/whatsapp/model/message/model/KeepInChat.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.message.model; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; 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.util.Clock; @@ -14,7 +13,7 @@ /** * A model class that represents an ephemeral message that was saved manually by the user in a chat */ -@ProtobufMessageName("KeepInChat") +@ProtobufMessage(name = "KeepInChat") public record KeepInChat( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) KeepInChatType keepType, @@ -28,7 +27,7 @@ public record KeepInChat( long clientTimestampInMilliseconds, @ProtobufProperty(index = 6, type = ProtobufType.INT64) long serverTimestampMilliseconds -) implements ProtobufMessage { +) { public Optional serverTimestamp() { return Clock.parseSeconds(serverTimestampSeconds); } diff --git a/src/main/java/it/auties/whatsapp/model/message/model/KeepInChatType.java b/src/main/java/it/auties/whatsapp/model/message/model/KeepInChatType.java index 6a144700b..de40acd33 100644 --- a/src/main/java/it/auties/whatsapp/model/message/model/KeepInChatType.java +++ b/src/main/java/it/auties/whatsapp/model/message/model/KeepInChatType.java @@ -1,11 +1,11 @@ package it.auties.whatsapp.model.message.model; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; -import it.auties.protobuf.model.ProtobufEnum; +import it.auties.protobuf.annotation.ProtobufMessage; -@ProtobufMessageName("KeepType") -public enum KeepInChatType implements ProtobufEnum { +@ProtobufEnum(name = "KeepType") +public enum KeepInChatType { UNKNOWN(0), KEEP_FOR_ALL(1), UNDO_KEEP_FOR_ALL(2); diff --git a/src/main/java/it/auties/whatsapp/model/message/model/Message.java b/src/main/java/it/auties/whatsapp/model/message/model/Message.java index 662cae3a3..9dba5da45 100644 --- a/src/main/java/it/auties/whatsapp/model/message/model/Message.java +++ b/src/main/java/it/auties/whatsapp/model/message/model/Message.java @@ -1,12 +1,11 @@ package it.auties.whatsapp.model.message.model; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.whatsapp.model.message.standard.*; /** * A model interface that represents a message sent by a contact or by Whatsapp. */ -public sealed interface Message extends ProtobufMessage permits ButtonMessage, ContextualMessage, PaymentMessage, ServerMessage, CallMessage, EmptyMessage, KeepInChatMessage, NewsletterAdminInviteMessage, PollUpdateMessage, ReactionMessage { +public sealed interface Message permits ButtonMessage, ContextualMessage, PaymentMessage, ServerMessage, CallMessage, EmptyMessage, KeepInChatMessage, NewsletterAdminInviteMessage, PollUpdateMessage, ReactionMessage { /** * Return message type * diff --git a/src/main/java/it/auties/whatsapp/model/message/model/MessageContainer.java b/src/main/java/it/auties/whatsapp/model/message/model/MessageContainer.java index 421c4b509..26710451a 100644 --- a/src/main/java/it/auties/whatsapp/model/message/model/MessageContainer.java +++ b/src/main/java/it/auties/whatsapp/model/message/model/MessageContainer.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.message.model; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.button.template.highlyStructured.HighlyStructuredMessage; import it.auties.whatsapp.model.info.DeviceContextInfo; @@ -17,9 +16,7 @@ /** * A container for all types of messages known currently to WhatsappWeb. *

- * Only one of these properties should be populated, however it's not certain as Whatsapp's Protobuf - * doesn't use a one of instruction as it would be logical to in said case. This may imply that in - * some particular and rare cases more than one property can be populated. + * Only one of these properties is populated usually, but it is possible to have multiple after a message retry for example *

* There are several categories of messages: *

    @@ -30,7 +27,7 @@ *
  • Standard messages
  • *
*/ -@ProtobufMessageName("Message") +@ProtobufMessage(name = "Message") public record MessageContainer( @ProtobufProperty(index = 1, type = ProtobufType.STRING) Optional textWithNoContextMessage, @@ -130,7 +127,7 @@ public record MessageContainer( Optional newsletterAdminInviteMessage, @ProtobufProperty(index = 35, type = ProtobufType.OBJECT) Optional deviceInfo -) implements ProtobufMessage { +) { /** * An empty message */ @@ -167,56 +164,58 @@ public static MessageContainerBuilder ofBuilder(T message) { var builder = new MessageContainerBuilder(); switch (message) { case SenderKeyDistributionMessage senderKeyDistribution -> - builder.senderKeyDistributionMessage(Optional.of(senderKeyDistribution)); - case ImageMessage image -> builder.imageMessage(Optional.of(image)); - case ContactMessage contact -> builder.contactMessage(Optional.of(contact)); - case LocationMessage location -> builder.locationMessage(Optional.of(location)); - case TextMessage text -> builder.textMessage(Optional.of(text)); - case DocumentMessage document -> builder.documentMessage(Optional.of(document)); - case AudioMessage audio -> builder.audioMessage(Optional.of(audio)); - case VideoOrGifMessage video -> builder.videoMessage(Optional.of(video)); - case ProtocolMessage protocol -> builder.protocolMessage(Optional.of(protocol)); - case ContactsMessage contactsArray -> builder.contactsArrayMessage(Optional.of(contactsArray)); + builder.senderKeyDistributionMessage(senderKeyDistribution); + case ImageMessage image -> builder.imageMessage(image); + case ContactMessage contact -> builder.contactMessage(contact); + case LocationMessage location -> builder.locationMessage(location); + case TextMessage text -> builder.textMessage(text); + case DocumentMessage document -> builder.documentMessage(document); + case AudioMessage audio -> builder.audioMessage(audio); + case VideoOrGifMessage video -> builder.videoMessage(video); + case ProtocolMessage protocol -> builder.protocolMessage(protocol); + case ContactsMessage contactsArray -> builder.contactsArrayMessage(contactsArray); case HighlyStructuredMessage highlyStructured -> - builder.highlyStructuredMessage(Optional.of(highlyStructured)); - case SendPaymentMessage sendPayment -> builder.sendPaymentMessage(Optional.of(sendPayment)); - case LiveLocationMessage liveLocation -> builder.liveLocationMessage(Optional.of(liveLocation)); - case RequestPaymentMessage requestPayment -> builder.requestPaymentMessage(Optional.of(requestPayment)); + builder.highlyStructuredMessage(highlyStructured); + case SendPaymentMessage sendPayment -> builder.sendPaymentMessage(sendPayment); + case LiveLocationMessage liveLocation -> builder.liveLocationMessage(liveLocation); + case RequestPaymentMessage requestPayment -> builder.requestPaymentMessage(requestPayment); case DeclinePaymentRequestMessage declinePaymentRequest -> - builder.declinePaymentRequestMessage(Optional.of(declinePaymentRequest)); + builder.declinePaymentRequestMessage(declinePaymentRequest); case CancelPaymentRequestMessage cancelPaymentRequest -> - builder.cancelPaymentRequestMessage(Optional.of(cancelPaymentRequest)); - case TemplateMessage template -> builder.templateMessage(Optional.of(template)); - case StickerMessage sticker -> builder.stickerMessage(Optional.of(sticker)); - case GroupInviteMessage groupInvite -> builder.groupInviteMessage(Optional.of(groupInvite)); + builder.cancelPaymentRequestMessage(cancelPaymentRequest); + case TemplateMessage template -> builder.templateMessage(template); + case StickerMessage sticker -> builder.stickerMessage(sticker); + case GroupInviteMessage groupInvite -> builder.groupInviteMessage(groupInvite); case TemplateReplyMessage templateButtonReply -> - builder.templateReplyMessage(Optional.of(templateButtonReply)); - case ProductMessage product -> builder.productMessage(Optional.of(product)); - case DeviceSyncMessage deviceSync -> builder.deviceSyncMessage(Optional.of(deviceSync)); - case ListMessage buttonsList -> builder.listMessage(Optional.of(buttonsList)); - case PaymentOrderMessage order -> builder.orderMessage(Optional.of(order)); - case ListResponseMessage listResponse -> builder.listResponseMessage(Optional.of(listResponse)); - case PaymentInvoiceMessage invoice -> builder.invoiceMessage(Optional.of(invoice)); - case ButtonsMessage buttons -> builder.buttonsMessage(Optional.of(buttons)); - case ButtonsResponseMessage buttonsResponse -> builder.buttonsResponseMessage(Optional.of(buttonsResponse)); - case PaymentInviteMessage paymentInvite -> builder.paymentInviteMessage(Optional.of(paymentInvite)); - case InteractiveMessage interactive -> builder.interactiveMessage(Optional.of(interactive)); - case ReactionMessage reaction -> builder.reactionMessage(Optional.of(reaction)); - case StickerSyncRMRMessage stickerSync -> builder.stickerSyncMessage(Optional.of(stickerSync)); - case DeviceSentMessage deviceSent -> builder.deviceSentMessage(Optional.of(deviceSent)); + builder.templateReplyMessage(templateButtonReply); + case ProductMessage product -> builder.productMessage(product); + case DeviceSyncMessage deviceSync -> builder.deviceSyncMessage(deviceSync); + case ListMessage buttonsList -> builder.listMessage(buttonsList); + case PaymentOrderMessage order -> builder.orderMessage(order); + case ListResponseMessage listResponse -> builder.listResponseMessage(listResponse); + case PaymentInvoiceMessage invoice -> builder.invoiceMessage(invoice); + case ButtonsMessage buttons -> builder.buttonsMessage(buttons); + case ButtonsResponseMessage buttonsResponse -> builder.buttonsResponseMessage(buttonsResponse); + case PaymentInviteMessage paymentInvite -> builder.paymentInviteMessage(paymentInvite); + case InteractiveMessage interactive -> builder.interactiveMessage(interactive); + case ReactionMessage reaction -> builder.reactionMessage(reaction); + case StickerSyncRMRMessage stickerSync -> builder.stickerSyncMessage(stickerSync); + case DeviceSentMessage deviceSent -> builder.deviceSentMessage(deviceSent); case InteractiveResponseMessage interactiveResponseMessage -> - builder.interactiveResponseMessage(Optional.of(interactiveResponseMessage)); + builder.interactiveResponseMessage(interactiveResponseMessage); case PollCreationMessage pollCreationMessage -> - builder.pollCreationMessage(Optional.of(pollCreationMessage)); - case PollUpdateMessage pollUpdateMessage -> builder.pollUpdateMessage(Optional.of(pollUpdateMessage)); - case KeepInChatMessage keepInChatMessage -> builder.keepInChatMessage(Optional.of(keepInChatMessage)); + builder.pollCreationMessage(pollCreationMessage); + case PollUpdateMessage pollUpdateMessage -> builder.pollUpdateMessage(pollUpdateMessage); + case KeepInChatMessage keepInChatMessage -> builder.keepInChatMessage(keepInChatMessage); case RequestPhoneNumberMessage requestPhoneNumberMessage -> - builder.requestPhoneNumberMessage(Optional.of(requestPhoneNumberMessage)); + builder.requestPhoneNumberMessage(requestPhoneNumberMessage); case EncryptedReactionMessage encReactionMessage -> - builder.encryptedReactionMessage(Optional.of(encReactionMessage)); - case CallMessage callMessage -> builder.callMessage(Optional.of(callMessage)); - case NewsletterAdminInviteMessage newsletterAdminInviteMessage -> builder.newsletterAdminInviteMessage(newsletterAdminInviteMessage); - default -> {} + builder.encryptedReactionMessage(encReactionMessage); + case CallMessage callMessage -> builder.callMessage(callMessage); + case NewsletterAdminInviteMessage newsletterAdminInviteMessage -> + builder.newsletterAdminInviteMessage(newsletterAdminInviteMessage); + default -> { + } } return builder; } @@ -303,9 +302,6 @@ public Message content() { if (this.textWithNoContextMessage.isPresent()) { return TextMessage.of(textWithNoContextMessage.get()); } - if (this.senderKeyDistributionMessage.isPresent()) { - return senderKeyDistributionMessage.get(); - } if (this.imageMessage.isPresent()) { return imageMessage.get(); } @@ -441,9 +437,13 @@ public Message content() { if (callMessage.isPresent()) { return callMessage.get(); } - if(newsletterAdminInviteMessage.isPresent()) { + if (newsletterAdminInviteMessage.isPresent()) { return newsletterAdminInviteMessage.get(); } + // This needs to be last + if (this.senderKeyDistributionMessage.isPresent()) { + return senderKeyDistributionMessage.get(); + } return EMPTY_MESSAGE; } @@ -484,7 +484,7 @@ public boolean hasCategory(MessageCategory category) { * @return a non-null type */ public MessageType type() { - if(textWithNoContextMessage.isPresent()) { + if (textWithNoContextMessage.isPresent()) { return MessageType.TEXT; } @@ -533,7 +533,7 @@ public MessageContainer toEphemeral() { return new MessageContainerBuilder() .ephemeralMessage(FutureMessageContainer.of(content())) - .deviceInfo(deviceInfo) + .deviceInfo(deviceInfo.orElse(null)) .build(); } @@ -549,7 +549,7 @@ public MessageContainer toViewOnce() { return new MessageContainerBuilder() .viewOnceMessage(FutureMessageContainer.of(content())) - .deviceInfo(deviceInfo) + .deviceInfo(deviceInfo.orElse(null)) .build(); } diff --git a/src/main/java/it/auties/whatsapp/model/message/model/MessageReceipt.java b/src/main/java/it/auties/whatsapp/model/message/model/MessageReceipt.java index afd3d6864..9147eff30 100644 --- a/src/main/java/it/auties/whatsapp/model/message/model/MessageReceipt.java +++ b/src/main/java/it/auties/whatsapp/model/message/model/MessageReceipt.java @@ -1,9 +1,8 @@ package it.auties.whatsapp.model.message.model; import com.fasterxml.jackson.annotation.JsonCreator; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; 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.util.Clock; @@ -17,8 +16,8 @@ /** * A model that represents the receipt for a message */ -@ProtobufMessageName("UserReceipt") -public final class MessageReceipt implements ProtobufMessage { +@ProtobufMessage(name = "UserReceipt") +public final class MessageReceipt { @ProtobufProperty(index = 2, type = ProtobufType.INT64) private Long deliveredTimestampSeconds; @ProtobufProperty(index = 3, type = ProtobufType.INT64) diff --git a/src/main/java/it/auties/whatsapp/model/message/model/MessageStatus.java b/src/main/java/it/auties/whatsapp/model/message/model/MessageStatus.java index e193cdc0a..a386054ab 100644 --- a/src/main/java/it/auties/whatsapp/model/message/model/MessageStatus.java +++ b/src/main/java/it/auties/whatsapp/model/message/model/MessageStatus.java @@ -1,7 +1,7 @@ package it.auties.whatsapp.model.message.model; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; import java.util.Arrays; import java.util.Optional; @@ -9,7 +9,8 @@ /** * The constants of this enumerated type describe the various types of status of a {@link Message} */ -public enum MessageStatus implements ProtobufEnum { +@ProtobufEnum +public enum MessageStatus { /** * Erroneous status(no ticks) */ diff --git a/src/main/java/it/auties/whatsapp/model/message/model/PublicServiceAnnouncementStatus.java b/src/main/java/it/auties/whatsapp/model/message/model/PublicServiceAnnouncementStatus.java index befd467a9..6fa7f1681 100644 --- a/src/main/java/it/auties/whatsapp/model/message/model/PublicServiceAnnouncementStatus.java +++ b/src/main/java/it/auties/whatsapp/model/message/model/PublicServiceAnnouncementStatus.java @@ -1,21 +1,20 @@ package it.auties.whatsapp.model.message.model; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.util.Clock; import java.time.ZonedDateTime; import java.util.Optional; -@ProtobufMessageName("StatusPSA") +@ProtobufMessage(name = "StatusPSA") public record PublicServiceAnnouncementStatus( @ProtobufProperty(index = 44, type = ProtobufType.STRING) String campaignId, @ProtobufProperty(index = 45, type = ProtobufType.UINT64) long campaignExpirationTimestampSeconds -) implements ProtobufMessage { +) { public Optional campaignExpirationTimestamp() { return Clock.parseSeconds(campaignExpirationTimestampSeconds); } diff --git a/src/main/java/it/auties/whatsapp/model/message/payment/CancelPaymentRequestMessage.java b/src/main/java/it/auties/whatsapp/model/message/payment/CancelPaymentRequestMessage.java index 2d718fc75..5732de31a 100644 --- a/src/main/java/it/auties/whatsapp/model/message/payment/CancelPaymentRequestMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/payment/CancelPaymentRequestMessage.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.message.payment; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.message.model.ChatMessageKey; @@ -10,7 +10,7 @@ /** * A model class that represents a message that cancels a {@link RequestPaymentMessage}. */ -@ProtobufMessageName("Message.CancelPaymentRequestMessage") +@ProtobufMessage(name = "Message.CancelPaymentRequestMessage") public record CancelPaymentRequestMessage( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) ChatMessageKey key diff --git a/src/main/java/it/auties/whatsapp/model/message/payment/DeclinePaymentRequestMessage.java b/src/main/java/it/auties/whatsapp/model/message/payment/DeclinePaymentRequestMessage.java index ca43540b8..cd3633375 100644 --- a/src/main/java/it/auties/whatsapp/model/message/payment/DeclinePaymentRequestMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/payment/DeclinePaymentRequestMessage.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.message.payment; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.message.model.ChatMessageKey; @@ -10,7 +10,7 @@ /** * A model class that represents a message to decline a {@link RequestPaymentMessage}. */ -@ProtobufMessageName("Message.DeclinePaymentRequestMessage") +@ProtobufMessage(name = "Message.DeclinePaymentRequestMessage") public record DeclinePaymentRequestMessage( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) ChatMessageKey key diff --git a/src/main/java/it/auties/whatsapp/model/message/payment/PaymentInviteMessage.java b/src/main/java/it/auties/whatsapp/model/message/payment/PaymentInviteMessage.java index c9d11fbd8..22cef77d6 100644 --- a/src/main/java/it/auties/whatsapp/model/message/payment/PaymentInviteMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/payment/PaymentInviteMessage.java @@ -1,9 +1,9 @@ package it.auties.whatsapp.model.message.payment; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.message.model.MessageType; import it.auties.whatsapp.model.message.model.PaymentMessage; @@ -13,7 +13,7 @@ /** * A model class that represents a message to decline a {@link RequestPaymentMessage}. */ -@ProtobufMessageName("Message.PaymentInviteMessage") +@ProtobufMessage(name = "Message.PaymentInviteMessage") public record PaymentInviteMessage( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) ServiceType serviceType, @@ -25,8 +25,8 @@ public MessageType type() { return MessageType.PAYMENT_INVITE; } - @ProtobufMessageName("Message.PaymentInviteMessage.ServiceType") - public enum ServiceType implements ProtobufEnum { + @ProtobufEnum(name = "Message.PaymentInviteMessage.ServiceType") + public enum ServiceType { /** * Unknown service provider */ diff --git a/src/main/java/it/auties/whatsapp/model/message/payment/PaymentInvoiceMessage.java b/src/main/java/it/auties/whatsapp/model/message/payment/PaymentInvoiceMessage.java index c07166b34..444ad1d97 100644 --- a/src/main/java/it/auties/whatsapp/model/message/payment/PaymentInvoiceMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/payment/PaymentInvoiceMessage.java @@ -1,9 +1,10 @@ package it.auties.whatsapp.model.message.payment; import com.fasterxml.jackson.annotation.JsonCreator; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.info.ContextInfo; import it.auties.whatsapp.model.media.AttachmentType; @@ -18,36 +19,28 @@ /** * A model class that represents a message to notify the invoice about a successful payment. */ +@ProtobufMessage public final class PaymentInvoiceMessage implements PaymentMessage, MediaMessage { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private final String note; @ProtobufProperty(index = 2, type = ProtobufType.STRING) private final String token; - @ProtobufProperty(index = 3, type = ProtobufType.OBJECT) private final PaymentAttachmentType paymentAttachmentType; - @ProtobufProperty(index = 4, type = ProtobufType.STRING) private final String mimeType; - @ProtobufProperty(index = 5, type = ProtobufType.BYTES) private byte[] mediaKey; - @ProtobufProperty(index = 6, type = ProtobufType.UINT64) private Long mediaKeyTimestampSeconds; - @ProtobufProperty(index = 7, type = ProtobufType.BYTES) private byte[] mediaSha256; - @ProtobufProperty(index = 8, type = ProtobufType.BYTES) private byte[] mediaEncryptedSha256; - @ProtobufProperty(index = 9, type = ProtobufType.STRING) private String mediaDirectPath; - @ProtobufProperty(index = 10, type = ProtobufType.BYTES) private final byte[] thumbnail; - @ProtobufProperty(index = 17, type = ProtobufType.OBJECT) private ContextInfo contextInfo; @@ -202,7 +195,8 @@ public PaymentInvoiceMessage setContextInfo(ContextInfo contextInfo) { * The constants of this enumerated type describe the various types of attachment that an invoice * can wrap */ - public enum PaymentAttachmentType implements ProtobufEnum { + @ProtobufEnum + public enum PaymentAttachmentType { /** * Image */ diff --git a/src/main/java/it/auties/whatsapp/model/message/payment/PaymentOrderMessage.java b/src/main/java/it/auties/whatsapp/model/message/payment/PaymentOrderMessage.java index d1fa8e869..2384bfed6 100644 --- a/src/main/java/it/auties/whatsapp/model/message/payment/PaymentOrderMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/payment/PaymentOrderMessage.java @@ -1,9 +1,9 @@ package it.auties.whatsapp.model.message.payment; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.info.ContextInfo; import it.auties.whatsapp.model.jid.Jid; @@ -18,7 +18,7 @@ /** * A model class that represents a message to pay an order. */ -@ProtobufMessageName("Message.PaymentOrderMessage") +@ProtobufMessage(name = "Message.PaymentOrderMessage") public final class PaymentOrderMessage implements ContextualMessage, PaymentMessage { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private final String id; @@ -138,8 +138,8 @@ public String toString() { } - @ProtobufMessageName("Message.OrderMessage.OrderStatus") - public enum Status implements ProtobufEnum { + @ProtobufEnum(name = "Message.OrderMessage.OrderStatus") + public enum Status { /** * Inquiry */ @@ -156,8 +156,8 @@ public int index() { } } - @ProtobufMessageName("Message.OrderMessage.OrderSurface") - public enum PaymentOrderSurface implements ProtobufEnum { + @ProtobufEnum(name = "Message.OrderMessage.OrderSurface") + public enum PaymentOrderSurface { /** * Catalog */ diff --git a/src/main/java/it/auties/whatsapp/model/message/payment/RequestPaymentMessage.java b/src/main/java/it/auties/whatsapp/model/message/payment/RequestPaymentMessage.java index f7f9afa0d..791bd6036 100644 --- a/src/main/java/it/auties/whatsapp/model/message/payment/RequestPaymentMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/payment/RequestPaymentMessage.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.message.payment; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.jid.Jid; @@ -18,7 +18,7 @@ /** * A model class that represents a message to try to place a {@link PaymentMessage}. */ -@ProtobufMessageName("Message.RequestPaymentMessage") +@ProtobufMessage(name = "Message.RequestPaymentMessage") public record RequestPaymentMessage( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String currency, diff --git a/src/main/java/it/auties/whatsapp/model/message/payment/SendPaymentMessage.java b/src/main/java/it/auties/whatsapp/model/message/payment/SendPaymentMessage.java index f475fd7f7..d2a7f5dc8 100644 --- a/src/main/java/it/auties/whatsapp/model/message/payment/SendPaymentMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/payment/SendPaymentMessage.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.message.payment; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.message.model.ChatMessageKey; @@ -14,7 +14,7 @@ /** * A model class that represents a message to confirm a {@link RequestPaymentMessage}. */ -@ProtobufMessageName("Message.SendPaymentMessage") +@ProtobufMessage(name = "Message.SendPaymentMessage") public record SendPaymentMessage( @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) Optional noteMessage, diff --git a/src/main/java/it/auties/whatsapp/model/message/server/DeviceSentMessage.java b/src/main/java/it/auties/whatsapp/model/message/server/DeviceSentMessage.java index f4b5e242b..8b3f047c6 100644 --- a/src/main/java/it/auties/whatsapp/model/message/server/DeviceSentMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/server/DeviceSentMessage.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.message.server; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.jid.Jid; @@ -14,7 +14,7 @@ * A model class that represents a message that refers to a message sent by the device paired with * the active WhatsappWeb session. */ -@ProtobufMessageName("Message.DeviceSentMessage") +@ProtobufMessage(name = "Message.DeviceSentMessage") public record DeviceSentMessage( @ProtobufProperty(index = 1, type = ProtobufType.STRING) Jid destinationJid, diff --git a/src/main/java/it/auties/whatsapp/model/message/server/DeviceSyncMessage.java b/src/main/java/it/auties/whatsapp/model/message/server/DeviceSyncMessage.java index d908c03af..9c20c8ed7 100644 --- a/src/main/java/it/auties/whatsapp/model/message/server/DeviceSyncMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/server/DeviceSyncMessage.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.message.server; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.message.model.MessageType; @@ -10,7 +10,7 @@ * A model class that represents a message that refers to a message sent by the device paired with * the active WhatsappWeb session to dataSync. */ -@ProtobufMessageName("Message.DeviceSyncMessage") +@ProtobufMessage(name = "Message.DeviceSyncMessage") public record DeviceSyncMessage( @ProtobufProperty(index = 1, type = ProtobufType.BYTES) byte[] serializedXmlBytes diff --git a/src/main/java/it/auties/whatsapp/model/message/server/ProtocolMessage.java b/src/main/java/it/auties/whatsapp/model/message/server/ProtocolMessage.java index 9daa02c8c..edfbf0fb8 100644 --- a/src/main/java/it/auties/whatsapp/model/message/server/ProtocolMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/server/ProtocolMessage.java @@ -1,9 +1,9 @@ package it.auties.whatsapp.model.message.server; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.chat.ChatDisappear; import it.auties.whatsapp.model.message.model.ChatMessageKey; @@ -18,7 +18,7 @@ /** * A model class that represents a message sent by a WhatsappWeb. */ -@ProtobufMessageName("Message.ProtocolMessage") +@ProtobufMessage(name = "Message.ProtocolMessage") public record ProtocolMessage( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) Optional key, @@ -54,8 +54,8 @@ public MessageType type() { * The constants of this enumerated type describe the various type of data that a * {@link ProtocolMessage} can wrap */ - @ProtobufMessageName("Message.ProtocolMessage.Type") - public enum Type implements ProtobufEnum { + @ProtobufEnum(name = "Message.ProtocolMessage.Type") + public enum Type { /** * A {@link ProtocolMessage} that notifies that a message was deleted for everyone in a chat */ diff --git a/src/main/java/it/auties/whatsapp/model/message/server/SenderKeyDistributionMessage.java b/src/main/java/it/auties/whatsapp/model/message/server/SenderKeyDistributionMessage.java index fe5f21da4..a95bb92b6 100644 --- a/src/main/java/it/auties/whatsapp/model/message/server/SenderKeyDistributionMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/server/SenderKeyDistributionMessage.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.message.server; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.message.model.MessageType; @@ -12,7 +12,7 @@ * href="https://archive.kaidan.im/libsignal-protocol-c-docs/html/struct___textsecure_____sender_key_distribution_message.html">their * documentation */ -@ProtobufMessageName("Message.SenderKeyDistributionMessage") +@ProtobufMessage(name = "Message.SenderKeyDistributionMessage") public record SenderKeyDistributionMessage( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String groupId, diff --git a/src/main/java/it/auties/whatsapp/model/message/server/StickerSyncRMRMessage.java b/src/main/java/it/auties/whatsapp/model/message/server/StickerSyncRMRMessage.java index 8e08b0ea5..79a3199a4 100644 --- a/src/main/java/it/auties/whatsapp/model/message/server/StickerSyncRMRMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/server/StickerSyncRMRMessage.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.message.server; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.message.model.MessageType; @@ -8,7 +8,7 @@ import java.util.List; -@ProtobufMessageName("Message.StickerSyncRMRMessage") +@ProtobufMessage(name = "Message.StickerSyncRMRMessage") public record StickerSyncRMRMessage( @ProtobufProperty(index = 1, type = ProtobufType.STRING) List hash, diff --git a/src/main/java/it/auties/whatsapp/model/message/standard/AudioMessage.java b/src/main/java/it/auties/whatsapp/model/message/standard/AudioMessage.java index a47efc10b..bfa325047 100644 --- a/src/main/java/it/auties/whatsapp/model/message/standard/AudioMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/standard/AudioMessage.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import it.auties.protobuf.annotation.ProtobufBuilder; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.info.ContextInfo; @@ -19,47 +19,34 @@ import java.util.OptionalInt; import java.util.OptionalLong; -@ProtobufMessageName("Message.AudioMessage") +@ProtobufMessage(name = "Message.AudioMessage") public final class AudioMessage extends ExtendedMediaMessage implements MediaMessage { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private String mediaUrl; - @ProtobufProperty(index = 2, type = ProtobufType.STRING) private final String mimetype; - @ProtobufProperty(index = 3, type = ProtobufType.BYTES) private byte[] mediaSha256; - @ProtobufProperty(index = 4, type = ProtobufType.UINT64) private Long mediaSize; - @ProtobufProperty(index = 5, type = ProtobufType.UINT32) private final Integer duration; - @ProtobufProperty(index = 6, type = ProtobufType.BOOL) private final boolean voiceMessage; - @ProtobufProperty(index = 7, type = ProtobufType.BYTES) private byte[] mediaKey; - @ProtobufProperty(index = 8, type = ProtobufType.BYTES) private byte[] mediaEncryptedSha256; - @ProtobufProperty(index = 9, type = ProtobufType.STRING) private String mediaDirectPath; - @ProtobufProperty(index = 10, type = ProtobufType.INT64) private Long mediaKeyTimestampSeconds; - @ProtobufProperty(index = 17, type = ProtobufType.OBJECT) private ContextInfo contextInfo; - @ProtobufProperty(index = 18, type = ProtobufType.BYTES) private final byte[] streamingSidecar; - @ProtobufProperty(index = 19, type = ProtobufType.BYTES) private final byte[] waveform; - @ProtobufProperty(index = 20, type = ProtobufType.FIXED32) private final Integer backgroundArgb; diff --git a/src/main/java/it/auties/whatsapp/model/message/standard/CallMessage.java b/src/main/java/it/auties/whatsapp/model/message/standard/CallMessage.java index a3a41ac51..ddc2ddc83 100644 --- a/src/main/java/it/auties/whatsapp/model/message/standard/CallMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/standard/CallMessage.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.message.standard; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.message.model.Message; @@ -11,7 +11,7 @@ /** * A message that contains information related to a call */ -@ProtobufMessageName("Message.Call") +@ProtobufMessage(name = "Message.Call") public record CallMessage( @ProtobufProperty(index = 1, type = ProtobufType.BYTES) byte[] key, diff --git a/src/main/java/it/auties/whatsapp/model/message/standard/ContactMessage.java b/src/main/java/it/auties/whatsapp/model/message/standard/ContactMessage.java index c09e24f7b..830d5872b 100644 --- a/src/main/java/it/auties/whatsapp/model/message/standard/ContactMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/standard/ContactMessage.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.message.standard; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.contact.ContactCard; @@ -14,7 +14,7 @@ /** * A model class that represents a message holding a contact inside */ -@ProtobufMessageName("Message.ContactMessage") +@ProtobufMessage(name = "Message.ContactMessage") public final class ContactMessage implements ContextualMessage { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private final String name; @@ -23,16 +23,16 @@ public final class ContactMessage implements ContextualMessage { @ProtobufProperty(index = 17, type = ProtobufType.OBJECT) private ContextInfo contextInfo; - public static ContactMessage of(String name, ContactCard vcard) { - return new ContactMessage(name, vcard, null); - } - public ContactMessage(String name, ContactCard vcard, ContextInfo contextInfo) { this.name = name; this.vcard = vcard; this.contextInfo = contextInfo; } + public static ContactMessage of(String name, ContactCard vcard) { + return new ContactMessage(name, vcard, null); + } + @Override public MessageType type() { return MessageType.CONTACT; diff --git a/src/main/java/it/auties/whatsapp/model/message/standard/ContactsMessage.java b/src/main/java/it/auties/whatsapp/model/message/standard/ContactsMessage.java index e3bb4f731..8ec68b242 100644 --- a/src/main/java/it/auties/whatsapp/model/message/standard/ContactsMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/standard/ContactsMessage.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.message.standard; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.info.ContextInfo; @@ -14,7 +14,7 @@ /** * A model class that represents a message holding a list of contacts inside */ -@ProtobufMessageName("Message.ContactsArrayMessage") +@ProtobufMessage(name = "Message.ContactsArrayMessage") public final class ContactsMessage implements ContextualMessage { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private final String name; diff --git a/src/main/java/it/auties/whatsapp/model/message/standard/DocumentMessage.java b/src/main/java/it/auties/whatsapp/model/message/standard/DocumentMessage.java index 4ae938d71..60142c699 100644 --- a/src/main/java/it/auties/whatsapp/model/message/standard/DocumentMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/standard/DocumentMessage.java @@ -2,6 +2,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import it.auties.protobuf.annotation.ProtobufBuilder; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.button.interactive.InteractiveHeaderAttachment; @@ -15,7 +16,6 @@ import it.auties.whatsapp.model.message.model.reserved.ExtendedMediaMessage; import it.auties.whatsapp.util.Clock; import it.auties.whatsapp.util.Medias; -import it.auties.whatsapp.util.Specification; import it.auties.whatsapp.util.Validate; import java.time.ZonedDateTime; @@ -29,70 +29,55 @@ /** * A model class that represents a message holding a document inside */ +@ProtobufMessage public final class DocumentMessage extends ExtendedMediaMessage implements MediaMessage, InteractiveHeaderAttachment, ButtonsMessageHeader, HighlyStructuredFourRowTemplateTitle, HydratedFourRowTemplateTitle { - @ProtobufProperty(index = 1, type = ProtobufType.STRING) - private String mediaUrl; + private static final int THUMBNAIL_WIDTH = 480; + private static final int THUMBNAIL_HEIGHT = 339; + @ProtobufProperty(index = 1, type = ProtobufType.STRING) + String mediaUrl; @ProtobufProperty(index = 2, type = ProtobufType.STRING) - private final String mimetype; - + final String mimetype; @ProtobufProperty(index = 3, type = ProtobufType.STRING) - private final String title; - + final String title; @ProtobufProperty(index = 4, type = ProtobufType.BYTES) - private byte[] mediaSha256; - + byte[] mediaSha256; @ProtobufProperty(index = 5, type = ProtobufType.UINT64) - private Long mediaSize; - + Long mediaSize; @ProtobufProperty(index = 6, type = ProtobufType.UINT32) - private final Integer pageCount; - + final Integer pageCount; @ProtobufProperty(index = 7, type = ProtobufType.BYTES) - private byte[] mediaKey; - + byte[] mediaKey; @ProtobufProperty(index = 8, type = ProtobufType.STRING) - private final String fileName; - + final String fileName; @ProtobufProperty(index = 9, type = ProtobufType.BYTES) - private byte[] mediaEncryptedSha256; - + byte[] mediaEncryptedSha256; @ProtobufProperty(index = 10, type = ProtobufType.STRING) - private String mediaDirectPath; - + String mediaDirectPath; @ProtobufProperty(index = 11, type = ProtobufType.UINT64) - private Long mediaKeyTimestampSeconds; - - @ProtobufProperty(index = 16, type = ProtobufType.BYTES) - private final byte[] thumbnail; - + Long mediaKeyTimestampSeconds; @ProtobufProperty(index = 12, type = ProtobufType.BOOL) - private final boolean contactVcard; - + final boolean contactVcard; @ProtobufProperty(index = 13, type = ProtobufType.STRING) - private final String thumbnailDirectPath; - + final String thumbnailDirectPath; @ProtobufProperty(index = 14, type = ProtobufType.BYTES) - private final byte[] thumbnailSha256; - + final byte[] thumbnailSha256; @ProtobufProperty(index = 15, type = ProtobufType.BYTES) - private final byte[] thumbnailEncSha256; - + final byte[] thumbnailEncSha256; + @ProtobufProperty(index = 16, type = ProtobufType.BYTES) + final byte[] thumbnail; @ProtobufProperty(index = 17, type = ProtobufType.OBJECT) - private ContextInfo contextInfo; - + ContextInfo contextInfo; @ProtobufProperty(index = 18, type = ProtobufType.UINT32) - private final Integer thumbnailHeight; - + final Integer thumbnailHeight; @ProtobufProperty(index = 19, type = ProtobufType.UINT32) - private final Integer thumbnailWidth; - + final Integer thumbnailWidth; @ProtobufProperty(index = 20, type = ProtobufType.STRING) - private final String caption; + final String caption; @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - public DocumentMessage(String mediaUrl, String mimetype, String title, byte[] mediaSha256, Long mediaSize, Integer pageCount, byte[] mediaKey, String fileName, byte[] mediaEncryptedSha256, String mediaDirectPath, Long mediaKeyTimestampSeconds, byte[] thumbnail, boolean contactVcard, String thumbnailDirectPath, byte[] thumbnailSha256, byte[] thumbnailEncSha256, ContextInfo contextInfo, Integer thumbnailHeight, Integer thumbnailWidth, String caption) { + public DocumentMessage(String mediaUrl, String mimetype, String title, byte[] mediaSha256, Long mediaSize, Integer pageCount, byte[] mediaKey, String fileName, byte[] mediaEncryptedSha256, String mediaDirectPath, Long mediaKeyTimestampSeconds, boolean contactVcard, String thumbnailDirectPath, byte[] thumbnailSha256, byte[] thumbnailEncSha256, byte[] thumbnail, ContextInfo contextInfo, Integer thumbnailHeight, Integer thumbnailWidth, String caption) { this.mediaUrl = mediaUrl; this.mimetype = mimetype; this.title = title; @@ -104,11 +89,11 @@ public DocumentMessage(String mediaUrl, String mimetype, String title, byte[] me this.mediaEncryptedSha256 = mediaEncryptedSha256; this.mediaDirectPath = mediaDirectPath; this.mediaKeyTimestampSeconds = mediaKeyTimestampSeconds; - this.thumbnail = thumbnail; this.contactVcard = contactVcard; this.thumbnailDirectPath = thumbnailDirectPath; this.thumbnailSha256 = thumbnailSha256; this.thumbnailEncSha256 = thumbnailEncSha256; + this.thumbnail = thumbnail; this.contextInfo = contextInfo; this.thumbnailHeight = thumbnailHeight; this.thumbnailWidth = thumbnailWidth; @@ -126,8 +111,8 @@ static DocumentMessage customBuilder(byte[] media, String fileName, String mimeT .pageCount(pageCount > 0 ? pageCount : Medias.getPagesCount(media).orElse(1)) .title(title) .thumbnail(thumbnail != null ? null : Medias.getDocumentThumbnail(media).orElse(null)) - .thumbnailWidth(Specification.Whatsapp.THUMBNAIL_WIDTH) - .thumbnailHeight(Specification.Whatsapp.THUMBNAIL_HEIGHT) + .thumbnailWidth(THUMBNAIL_WIDTH) + .thumbnailHeight(THUMBNAIL_HEIGHT) .contextInfo(Objects.requireNonNullElseGet(contextInfo, ContextInfo::empty)) .build() .setDecodedMedia(media); diff --git a/src/main/java/it/auties/whatsapp/model/message/standard/EncryptedReactionMessage.java b/src/main/java/it/auties/whatsapp/model/message/standard/EncryptedReactionMessage.java index eb3ab49ce..8f6855058 100644 --- a/src/main/java/it/auties/whatsapp/model/message/standard/EncryptedReactionMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/standard/EncryptedReactionMessage.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.message.standard; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.message.model.ChatMessageKey; @@ -8,7 +8,7 @@ import it.auties.whatsapp.model.message.model.MessageType; import it.auties.whatsapp.model.message.model.ServerMessage; -@ProtobufMessageName("Message.EncReactionMessage") +@ProtobufMessage(name = "Message.EncReactionMessage") public record EncryptedReactionMessage( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) ChatMessageKey targetMessageKey, diff --git a/src/main/java/it/auties/whatsapp/model/message/standard/GroupInviteMessage.java b/src/main/java/it/auties/whatsapp/model/message/standard/GroupInviteMessage.java index e484e0151..4de18b7bf 100644 --- a/src/main/java/it/auties/whatsapp/model/message/standard/GroupInviteMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/standard/GroupInviteMessage.java @@ -1,9 +1,9 @@ package it.auties.whatsapp.model.message.standard; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.info.ContextInfo; import it.auties.whatsapp.model.jid.Jid; @@ -20,7 +20,7 @@ /** * A model class that represents a message holding a whatsapp group invite inside */ -@ProtobufMessageName("Message.GroupInviteMessage") +@ProtobufMessage(name = "Message.GroupInviteMessage") public final class GroupInviteMessage implements ContextualMessage { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private final Jid group; @@ -38,7 +38,7 @@ public final class GroupInviteMessage implements ContextualMessage implements MediaMessage, InteractiveHeaderAttachment, ButtonsMessageHeader, HighlyStructuredFourRowTemplateTitle, HydratedFourRowTemplateTitle { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private String mediaUrl; - @ProtobufProperty(index = 2, type = ProtobufType.STRING) private final String mimetype; - @ProtobufProperty(index = 3, type = ProtobufType.STRING) private final String caption; - @ProtobufProperty(index = 4, type = ProtobufType.BYTES) private byte[] mediaSha256; - @ProtobufProperty(index = 5, type = ProtobufType.UINT64) private Long mediaSize; - @ProtobufProperty(index = 6, type = ProtobufType.UINT32) private final Integer height; - @ProtobufProperty(index = 7, type = ProtobufType.UINT32) private final Integer width; - @ProtobufProperty(index = 8, type = ProtobufType.BYTES) private byte[] mediaKey; - @ProtobufProperty(index = 9, type = ProtobufType.BYTES) private byte[] mediaEncryptedSha256; - @ProtobufProperty(index = 10, type = ProtobufType.OBJECT) private final List interactiveAnnotations; - @ProtobufProperty(index = 11, type = ProtobufType.STRING) private String mediaDirectPath; - @ProtobufProperty(index = 12, type = ProtobufType.UINT64) private Long mediaKeyTimestampSeconds; - @ProtobufProperty(index = 16, type = ProtobufType.BYTES) private final byte[] thumbnail; - @ProtobufProperty(index = 17, type = ProtobufType.OBJECT) private ContextInfo contextInfo; - @ProtobufProperty(index = 18, type = ProtobufType.BYTES) private final byte[] firstScanSidecar; - @ProtobufProperty(index = 19, type = ProtobufType.UINT32) private final Integer firstScanLength; - @ProtobufProperty(index = 20, type = ProtobufType.UINT32) private final Integer experimentGroupId; - @ProtobufProperty(index = 21, type = ProtobufType.BYTES) private final byte[] scansSidecar; - @ProtobufProperty(index = 22, type = ProtobufType.UINT32) private final List scanLengths; - @ProtobufProperty(index = 23, type = ProtobufType.BYTES) private final byte[] midQualityFileSha256; - @ProtobufProperty(index = 24, type = ProtobufType.BYTES) private final byte[] midQualityFileEncSha256; - @ProtobufProperty(index = 25, type = ProtobufType.BOOL) private final boolean viewOnce; - @ProtobufProperty(index = 26, type = ProtobufType.STRING) private final String thumbnailDirectPath; - @ProtobufProperty(index = 27, type = ProtobufType.BYTES) private final byte[] thumbnailSha256; - @ProtobufProperty(index = 28, type = ProtobufType.BYTES) private final byte[] thumbnailEncSha256; - @ProtobufProperty(index = 29, type = ProtobufType.STRING) private final String staticUrl; diff --git a/src/main/java/it/auties/whatsapp/model/message/standard/KeepInChatMessage.java b/src/main/java/it/auties/whatsapp/model/message/standard/KeepInChatMessage.java index 0bd86e70c..15853c0f7 100644 --- a/src/main/java/it/auties/whatsapp/model/message/standard/KeepInChatMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/standard/KeepInChatMessage.java @@ -1,12 +1,12 @@ package it.auties.whatsapp.model.message.standard; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.message.model.*; -@ProtobufMessageName("Message.KeepInChatMessage") +@ProtobufMessage(name = "Message.KeepInChatMessage") public record KeepInChatMessage( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) ChatMessageKey key, diff --git a/src/main/java/it/auties/whatsapp/model/message/standard/LiveLocationMessage.java b/src/main/java/it/auties/whatsapp/model/message/standard/LiveLocationMessage.java index d39dee56d..10999cb47 100644 --- a/src/main/java/it/auties/whatsapp/model/message/standard/LiveLocationMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/standard/LiveLocationMessage.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.message.standard; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.info.ContextInfo; @@ -15,7 +15,7 @@ /** * A model class that represents a message holding a live location inside */ -@ProtobufMessageName("Message.LiveLocationMessage") +@ProtobufMessage(name = "Message.LiveLocationMessage") public final class LiveLocationMessage implements ContextualMessage { @ProtobufProperty(index = 1, type = ProtobufType.DOUBLE) private final double latitude; diff --git a/src/main/java/it/auties/whatsapp/model/message/standard/LocationMessage.java b/src/main/java/it/auties/whatsapp/model/message/standard/LocationMessage.java index c4bde98e9..10dea0324 100644 --- a/src/main/java/it/auties/whatsapp/model/message/standard/LocationMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/standard/LocationMessage.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.message.standard; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.button.template.hsm.HighlyStructuredFourRowTemplateTitle; @@ -18,7 +18,7 @@ /** * A model class that represents a message holding a location inside */ -@ProtobufMessageName("Message.LocationMessage") +@ProtobufMessage(name = "Message.LocationMessage") public final class LocationMessage implements ContextualMessage, ButtonsMessageHeader, HighlyStructuredFourRowTemplateTitle, HydratedFourRowTemplateTitle { @ProtobufProperty(index = 1, type = ProtobufType.DOUBLE) private final double latitude; diff --git a/src/main/java/it/auties/whatsapp/model/message/standard/NewsletterAdminInviteMessage.java b/src/main/java/it/auties/whatsapp/model/message/standard/NewsletterAdminInviteMessage.java index 131c342e7..a50a904aa 100644 --- a/src/main/java/it/auties/whatsapp/model/message/standard/NewsletterAdminInviteMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/standard/NewsletterAdminInviteMessage.java @@ -1,5 +1,6 @@ package it.auties.whatsapp.model.message.standard; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.jid.Jid; @@ -11,6 +12,7 @@ import java.time.ZonedDateTime; import java.util.Optional; +@ProtobufMessage public record NewsletterAdminInviteMessage( @ProtobufProperty(index = 1, type = ProtobufType.STRING) Jid newsletterJid, diff --git a/src/main/java/it/auties/whatsapp/model/message/standard/PollCreationMessage.java b/src/main/java/it/auties/whatsapp/model/message/standard/PollCreationMessage.java index d88bc4bd3..174a13d1f 100644 --- a/src/main/java/it/auties/whatsapp/model/message/standard/PollCreationMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/standard/PollCreationMessage.java @@ -1,7 +1,7 @@ package it.auties.whatsapp.model.message.standard; import it.auties.protobuf.annotation.ProtobufBuilder; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.api.Whatsapp; @@ -23,26 +23,20 @@ /** * A model class that represents a message holding a poll inside */ -@ProtobufMessageName("Message.PollCreationMessage") +@ProtobufMessage(name = "Message.PollCreationMessage") public final class PollCreationMessage implements ContextualMessage { @ProtobufProperty(index = 1, type = ProtobufType.BYTES) private byte[] encryptionKey; - @ProtobufProperty(index = 2, type = ProtobufType.STRING) private final String title; - @ProtobufProperty(index = 3, type = ProtobufType.OBJECT) private final List selectableOptions; - @ProtobufProperty(index = 4, type = ProtobufType.UINT32) private final int selectableOptionsCount; - @ProtobufProperty(index = 5, type = ProtobufType.OBJECT) private ContextInfo contextInfo; - - @ProtobufProperty(index = 999, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.OBJECT) + @ProtobufProperty(index = 999, type = ProtobufType.MAP, mapKeyType = ProtobufType.STRING, mapValueType = ProtobufType.OBJECT) final Map selectableOptionsMap; - @ProtobufProperty(index = 1000, type = ProtobufType.OBJECT) final List selectedOptions; @@ -94,7 +88,7 @@ public Collection getSelectedOptions(JidProvider voter) { } public void addSelectedOptions(JidProvider voter, Collection voted) { - for(var entry : voted) { + for (var entry : voted) { var selectedPollOption = new SelectedPollOption(voter.toJid(), entry.name()); selectedOptions.add(selectedPollOption); } diff --git a/src/main/java/it/auties/whatsapp/model/message/standard/PollUpdateMessage.java b/src/main/java/it/auties/whatsapp/model/message/standard/PollUpdateMessage.java index 4269273bc..bb55e6a05 100644 --- a/src/main/java/it/auties/whatsapp/model/message/standard/PollUpdateMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/standard/PollUpdateMessage.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import it.auties.protobuf.annotation.ProtobufBuilder; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.api.Whatsapp; @@ -24,33 +24,32 @@ /** * A model class that represents a message holding a vote for a poll inside */ -@ProtobufMessageName("Message.PollUpdateMessage") +@ProtobufMessage(name = "Message.PollUpdateMessage") public final class PollUpdateMessage implements Message, EncryptedMessage { - private Jid voter; - @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) private final ChatMessageKey pollCreationMessageKey; - - private PollCreationMessage pollCreationMessage; - - private List votes; - @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) private PollUpdateEncryptedMetadata encryptedMetadata; - @ProtobufProperty(index = 3, type = ProtobufType.OBJECT) private final PollUpdateMessageMetadata metadata; - @ProtobufProperty(index = 4, type = ProtobufType.INT64) private final long senderTimestampMilliseconds; + @ProtobufProperty(index = 999, type = ProtobufType.STRING) + private Jid voter; + @ProtobufProperty(index = 1000, type = ProtobufType.OBJECT) + private PollCreationMessage pollCreationMessage; + @ProtobufProperty(index = 1001, type = ProtobufType.OBJECT) + private List votes; @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - public PollUpdateMessage(ChatMessageKey pollCreationMessageKey, PollUpdateEncryptedMetadata encryptedMetadata, PollUpdateMessageMetadata metadata, long senderTimestampMilliseconds) { + public PollUpdateMessage(ChatMessageKey pollCreationMessageKey, PollUpdateEncryptedMetadata encryptedMetadata, PollUpdateMessageMetadata metadata, long senderTimestampMilliseconds, Jid voter, PollCreationMessage pollCreationMessage, List votes) { this.pollCreationMessageKey = pollCreationMessageKey; this.encryptedMetadata = encryptedMetadata; this.metadata = metadata; this.senderTimestampMilliseconds = senderTimestampMilliseconds; - this.votes = new ArrayList<>(); + this.voter = voter; + this.pollCreationMessage = pollCreationMessage; + this.votes = votes; } /** diff --git a/src/main/java/it/auties/whatsapp/model/message/standard/ProductMessage.java b/src/main/java/it/auties/whatsapp/model/message/standard/ProductMessage.java index 707ac6f00..224f0b05a 100644 --- a/src/main/java/it/auties/whatsapp/model/message/standard/ProductMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/standard/ProductMessage.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.message.standard; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.info.ContextInfo; @@ -17,7 +17,7 @@ /** * A model class that represents a message holding a product inside */ -@ProtobufMessageName("Message.ProductMessage") +@ProtobufMessage(name = "Message.ProductMessage") public final class ProductMessage implements ContextualMessage, ButtonMessage { @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) private final Product product; diff --git a/src/main/java/it/auties/whatsapp/model/message/standard/ReactionMessage.java b/src/main/java/it/auties/whatsapp/model/message/standard/ReactionMessage.java index b8bc9969d..c1f84bc12 100644 --- a/src/main/java/it/auties/whatsapp/model/message/standard/ReactionMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/standard/ReactionMessage.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.message.standard; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.message.model.ChatMessageKey; @@ -16,7 +16,7 @@ /** * A model class that represents a message holding an emoji reaction inside */ -@ProtobufMessageName("Message.ReactionMessage") +@ProtobufMessage(name = "Message.ReactionMessage") public record ReactionMessage( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) ChatMessageKey key, diff --git a/src/main/java/it/auties/whatsapp/model/message/standard/RequestPhoneNumberMessage.java b/src/main/java/it/auties/whatsapp/model/message/standard/RequestPhoneNumberMessage.java index b3c43076a..b0dbcf804 100644 --- a/src/main/java/it/auties/whatsapp/model/message/standard/RequestPhoneNumberMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/standard/RequestPhoneNumberMessage.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.message.standard; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.info.ContextInfo; @@ -14,7 +14,7 @@ * A model class that represents a message holding a request for a phone number inside * Still needs to be implemented by Whatsapp */ -@ProtobufMessageName("Message.RequestPhoneNumberMessage") +@ProtobufMessage(name = "Message.RequestPhoneNumberMessage") public final class RequestPhoneNumberMessage implements ContextualMessage { @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) private ContextInfo contextInfo; diff --git a/src/main/java/it/auties/whatsapp/model/message/standard/StickerMessage.java b/src/main/java/it/auties/whatsapp/model/message/standard/StickerMessage.java index fceb2a2ee..ccf517984 100644 --- a/src/main/java/it/auties/whatsapp/model/message/standard/StickerMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/standard/StickerMessage.java @@ -2,7 +2,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import it.auties.protobuf.annotation.ProtobufBuilder; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.info.ContextInfo; @@ -24,56 +24,40 @@ /** * A model class that represents a message holding a sticker inside */ -@ProtobufMessageName("Message.StickerMessage") +@ProtobufMessage(name = "Message.StickerMessage") public final class StickerMessage extends ExtendedMediaMessage implements MediaMessage { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private String mediaUrl; - @ProtobufProperty(index = 2, type = ProtobufType.BYTES) private byte[] mediaSha256; - @ProtobufProperty(index = 3, type = ProtobufType.BYTES) private byte[] mediaEncryptedSha256; - @ProtobufProperty(index = 4, type = ProtobufType.BYTES) private byte[] mediaKey; - @ProtobufProperty(index = 5, type = ProtobufType.STRING) private final String mimetype; - @ProtobufProperty(index = 6, type = ProtobufType.UINT32) private final Integer height; - @ProtobufProperty(index = 7, type = ProtobufType.UINT32) private final Integer width; - @ProtobufProperty(index = 8, type = ProtobufType.STRING) private String mediaDirectPath; - @ProtobufProperty(index = 9, type = ProtobufType.UINT64) private Long mediaSize; - @ProtobufProperty(index = 10, type = ProtobufType.UINT64) private Long mediaKeyTimestampSeconds; - @ProtobufProperty(index = 11, type = ProtobufType.UINT32) private final Integer firstFrameLength; - @ProtobufProperty(index = 12, type = ProtobufType.BYTES) private final byte[] firstFrameSidecar; - @ProtobufProperty(index = 13, type = ProtobufType.BOOL) private final boolean animated; - @ProtobufProperty(index = 16, type = ProtobufType.BYTES) private final byte[] thumbnail; - @ProtobufProperty(index = 17, type = ProtobufType.OBJECT) private ContextInfo contextInfo; - @ProtobufProperty(index = 18, type = ProtobufType.INT64) private final Long stickerSentTimestamp; - @ProtobufProperty(index = 19, type = ProtobufType.BOOL) private final boolean avatar; diff --git a/src/main/java/it/auties/whatsapp/model/message/standard/TextMessage.java b/src/main/java/it/auties/whatsapp/model/message/standard/TextMessage.java index 1f27fa4a8..3def5ba4b 100644 --- a/src/main/java/it/auties/whatsapp/model/message/standard/TextMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/standard/TextMessage.java @@ -1,10 +1,10 @@ package it.auties.whatsapp.model.message.standard; import com.fasterxml.jackson.annotation.JsonCreator; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.info.ContextInfo; import it.auties.whatsapp.model.message.model.ContextualMessage; @@ -20,7 +20,7 @@ /** * A model class that represents a message holding text inside */ -@ProtobufMessageName("Message.TextMessage") +@ProtobufMessage(name = "Message.TextMessage") public final class TextMessage implements ContextualMessage { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private String text; @@ -355,8 +355,8 @@ public TextMessage setViewOnce(boolean viewOnce) { return this; } - @ProtobufMessageName("Message.TextMessage.InviteLinkGroupType") - public enum InviteLinkGroupType implements ProtobufEnum { + @ProtobufEnum(name = "Message.TextMessage.InviteLinkGroupType") + public enum InviteLinkGroupType { DEFAULT(0), PARENT(1), SUB(2), @@ -377,8 +377,8 @@ public int index() { * The constants of this enumerated type describe the various types of fonts that a * {@link TextMessage} supports. Not all clients currently display all fonts correctly. */ - @ProtobufMessageName("Message.TextMessage.FontType") - public enum FontType implements ProtobufEnum { + @ProtobufEnum(name = "Message.TextMessage.FontType") + public enum FontType { /** * Sans Serif */ @@ -419,8 +419,8 @@ public int index() { * The constants of this enumerated type describe the various types of previuew that a * {@link TextMessage} can provide. */ - @ProtobufMessageName("Message.TextMessage.PreviewType") - public enum PreviewType implements ProtobufEnum { + @ProtobufEnum(name = "Message.TextMessage.PreviewType") + public enum PreviewType { /** * No preview */ diff --git a/src/main/java/it/auties/whatsapp/model/message/standard/VideoOrGifMessage.java b/src/main/java/it/auties/whatsapp/model/message/standard/VideoOrGifMessage.java index 072369da4..74f58abae 100644 --- a/src/main/java/it/auties/whatsapp/model/message/standard/VideoOrGifMessage.java +++ b/src/main/java/it/auties/whatsapp/model/message/standard/VideoOrGifMessage.java @@ -2,9 +2,9 @@ import com.fasterxml.jackson.annotation.JsonCreator; import it.auties.protobuf.annotation.ProtobufBuilder; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufEnum; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.button.interactive.InteractiveHeaderAttachment; import it.auties.whatsapp.model.button.interactive.InteractiveLocationAnnotation; @@ -15,6 +15,7 @@ import it.auties.whatsapp.model.message.model.MediaMessage; import it.auties.whatsapp.model.message.model.MediaMessageType; import it.auties.whatsapp.model.message.model.reserved.ExtendedMediaMessage; +import it.auties.whatsapp.model.message.standard.VideoOrGifMessage.Attribution; import it.auties.whatsapp.util.Clock; import it.auties.whatsapp.util.Medias; import it.auties.whatsapp.util.Validate; @@ -28,75 +29,53 @@ /** * A model class that represents a message holding a video inside */ -@ProtobufMessageName("Message.VideoMessage") +@ProtobufMessage(name = "Message.VideoMessage") public final class VideoOrGifMessage extends ExtendedMediaMessage implements MediaMessage, InteractiveHeaderAttachment, ButtonsMessageHeader, HighlyStructuredFourRowTemplateTitle, HydratedFourRowTemplateTitle { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private String mediaUrl; - @ProtobufProperty(index = 2, type = ProtobufType.STRING) private final String mimetype; - @ProtobufProperty(index = 3, type = ProtobufType.BYTES) private byte[] mediaSha256; - @ProtobufProperty(index = 4, type = ProtobufType.UINT64) private Long mediaSize; - @ProtobufProperty(index = 5, type = ProtobufType.UINT32) private final Integer duration; - @ProtobufProperty(index = 6, type = ProtobufType.BYTES) private byte[] mediaKey; - @ProtobufProperty(index = 7, type = ProtobufType.STRING) private final String caption; - @ProtobufProperty(index = 8, type = ProtobufType.BOOL) private final boolean gifPlayback; - @ProtobufProperty(index = 9, type = ProtobufType.UINT32) private final Integer height; - @ProtobufProperty(index = 10, type = ProtobufType.UINT32) private final Integer width; - @ProtobufProperty(index = 11, type = ProtobufType.BYTES) private byte[] mediaEncryptedSha256; - @ProtobufProperty(index = 12, type = ProtobufType.OBJECT) private final List interactiveAnnotations; - @ProtobufProperty(index = 13, type = ProtobufType.STRING) private String mediaDirectPath; - @ProtobufProperty(index = 14, type = ProtobufType.INT64) private long mediaKeyTimestampSeconds; - @ProtobufProperty(index = 16, type = ProtobufType.BYTES) private final byte[] thumbnail; - @ProtobufProperty(index = 17, type = ProtobufType.OBJECT) private ContextInfo contextInfo; - @ProtobufProperty(index = 18, type = ProtobufType.BYTES) private final byte[] streamingSidecar; - @ProtobufProperty(index = 19, type = ProtobufType.OBJECT) private final Attribution gifAttribution; - @ProtobufProperty(index = 20, type = ProtobufType.BOOL) private final boolean viewOnce; - @ProtobufProperty(index = 21, type = ProtobufType.STRING) private final String thumbnailDirectPath; - @ProtobufProperty(index = 22, type = ProtobufType.BYTES) private final byte[] thumbnailSha256; - @ProtobufProperty(index = 23, type = ProtobufType.BYTES) private final byte[] thumbnailEncSha256; - @ProtobufProperty(index = 24, type = ProtobufType.STRING) private final String staticUrl; @@ -350,8 +329,8 @@ public VideoOrGifMessage setContextInfo(ContextInfo contextInfo) { * The constants of this enumerated type describe the various sources from where a gif can come * from */ - @ProtobufMessageName("Message.VideoMessage.Attribution") - public enum Attribution implements ProtobufEnum { + @ProtobufEnum(name = "Message.VideoMessage.Attribution") + public enum Attribution { /** * No source was specified */ diff --git a/src/main/java/it/auties/whatsapp/model/mobile/AccountInfo.java b/src/main/java/it/auties/whatsapp/model/mobile/AccountInfo.java new file mode 100644 index 000000000..40526e2e1 --- /dev/null +++ b/src/main/java/it/auties/whatsapp/model/mobile/AccountInfo.java @@ -0,0 +1,6 @@ +package it.auties.whatsapp.model.mobile; + +import java.time.ZonedDateTime; + +public record AccountInfo(ZonedDateTime lastRegistrationTimestamp, ZonedDateTime creationTimestamp) { +} diff --git a/src/main/java/it/auties/whatsapp/model/mobile/CountryLocale.java b/src/main/java/it/auties/whatsapp/model/mobile/CountryLocale.java index 6aa495fa5..197b565bb 100644 --- a/src/main/java/it/auties/whatsapp/model/mobile/CountryLocale.java +++ b/src/main/java/it/auties/whatsapp/model/mobile/CountryLocale.java @@ -1,12 +1,13 @@ package it.auties.whatsapp.model.mobile; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.Objects; import java.util.Optional; +@ProtobufMessage public record CountryLocale( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String languageValue, @@ -14,7 +15,7 @@ public record CountryLocale( String languageCode, @ProtobufProperty(index = 3, type = ProtobufType.STRING) String separator -) implements ProtobufMessage { +) { public static Optional of(String encoded) { return of(encoded, "-") .or(() -> of(encoded, "_")); diff --git a/src/main/java/it/auties/whatsapp/model/mobile/PhoneNumber.java b/src/main/java/it/auties/whatsapp/model/mobile/PhoneNumber.java index 7e17208b8..6092c9f32 100644 --- a/src/main/java/it/auties/whatsapp/model/mobile/PhoneNumber.java +++ b/src/main/java/it/auties/whatsapp/model/mobile/PhoneNumber.java @@ -3,7 +3,8 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; import com.google.i18n.phonenumbers.PhoneNumberUtil; -import it.auties.protobuf.annotation.ProtobufConverter; +import it.auties.protobuf.annotation.ProtobufDeserializer; +import it.auties.protobuf.annotation.ProtobufSerializer; import it.auties.whatsapp.model.jid.Jid; import java.util.Optional; @@ -21,7 +22,7 @@ public static PhoneNumber of(String phoneNumber) { return of(Long.parseLong(phoneNumber)); } - @ProtobufConverter + @ProtobufDeserializer @JsonCreator public static PhoneNumber of(long phoneNumber) { try { @@ -34,7 +35,7 @@ public static PhoneNumber of(long phoneNumber) { } } - @ProtobufConverter + @ProtobufSerializer public long number() { return Long.parseLong(countryCode.prefix() + numberWithoutPrefix); } diff --git a/src/main/java/it/auties/whatsapp/model/mobile/SixPartsKeys.java b/src/main/java/it/auties/whatsapp/model/mobile/SixPartsKeys.java index 1648a0f3b..f462e1202 100644 --- a/src/main/java/it/auties/whatsapp/model/mobile/SixPartsKeys.java +++ b/src/main/java/it/auties/whatsapp/model/mobile/SixPartsKeys.java @@ -8,10 +8,11 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -public record SixPartsKeys(PhoneNumber phoneNumber, SignalKeyPair noiseKeyPair, SignalKeyPair identityKeyPair, byte[] identityId) { +public record SixPartsKeys(PhoneNumber phoneNumber, SignalKeyPair noiseKeyPair, SignalKeyPair identityKeyPair, + byte[] identityId) { public static SixPartsKeys of(String sixParts) { Objects.requireNonNull(sixParts, "Invalid six parts"); - var parts = sixParts.trim().split(",", 6); + var parts = sixParts.replaceAll(" ", "").split(",", 6); Validate.isTrue(parts.length == 6, "Invalid format"); var phoneNumber = PhoneNumber.of(parts[0]); var noisePublicKey = Base64.getDecoder().decode(parts[1]); diff --git a/src/main/java/it/auties/whatsapp/model/newsletter/Newsletter.java b/src/main/java/it/auties/whatsapp/model/newsletter/Newsletter.java index 6ebb81370..2de80b70b 100644 --- a/src/main/java/it/auties/whatsapp/model/newsletter/Newsletter.java +++ b/src/main/java/it/auties/whatsapp/model/newsletter/Newsletter.java @@ -2,20 +2,21 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.info.NewsletterMessageInfo; import it.auties.whatsapp.model.jid.Jid; import it.auties.whatsapp.model.jid.JidProvider; -import it.auties.whatsapp.util.ConcurrentLinkedHashedDequeue; +import it.auties.whatsapp.util.ConcurrentLinkedSet; import java.util.Collection; import java.util.Collections; import java.util.Objects; import java.util.Optional; -public final class Newsletter implements JidProvider, ProtobufMessage { +@ProtobufMessage +public final class Newsletter implements JidProvider { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private final Jid jid; @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) @@ -25,7 +26,7 @@ public final class Newsletter implements JidProvider, ProtobufMessage { @ProtobufProperty(index = 4, type = ProtobufType.OBJECT) private final NewsletterViewerMetadata viewerMetadata; @ProtobufProperty(index = 5, type = ProtobufType.OBJECT) - private final ConcurrentLinkedHashedDequeue messages; + private final ConcurrentLinkedSet messages; @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) Newsletter( @@ -38,13 +39,13 @@ public final class Newsletter implements JidProvider, ProtobufMessage { @JsonProperty("viewer_metadata") NewsletterViewerMetadata viewerMetadata, @JsonProperty("messages") - ConcurrentLinkedHashedDequeue messages + ConcurrentLinkedSet messages ) { this.jid = jid; this.state = state; this.metadata = metadata; this.viewerMetadata = viewerMetadata; - this.messages = Objects.requireNonNullElseGet(messages, ConcurrentLinkedHashedDequeue::new); + this.messages = Objects.requireNonNullElseGet(messages, ConcurrentLinkedSet::new); } public Newsletter(Jid jid, NewsletterState state, NewsletterMetadata metadata, NewsletterViewerMetadata viewerMetadata) { @@ -52,7 +53,7 @@ public Newsletter(Jid jid, NewsletterState state, NewsletterMetadata metadata, N this.state = state; this.metadata = metadata; this.viewerMetadata = viewerMetadata; - this.messages = new ConcurrentLinkedHashedDequeue<>(); + this.messages = new ConcurrentLinkedSet<>(); } public void addMessage(NewsletterMessageInfo message) { diff --git a/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterDescription.java b/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterDescription.java index b59fb3f08..e33b5bc62 100644 --- a/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterDescription.java +++ b/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterDescription.java @@ -1,14 +1,15 @@ package it.auties.whatsapp.model.newsletter; import com.fasterxml.jackson.annotation.JsonProperty; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.util.Clock; import java.time.ZonedDateTime; import java.util.Optional; +@ProtobufMessage public record NewsletterDescription( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String id, @@ -17,7 +18,7 @@ public record NewsletterDescription( @ProtobufProperty(index = 3, type = ProtobufType.UINT64) @JsonProperty("update_time") long updateTimeSeconds -) implements ProtobufMessage { +) { public Optional updateTime() { return Clock.parseSeconds(updateTimeSeconds); } diff --git a/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterMetadata.java b/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterMetadata.java index 25c35f844..061f95eea 100644 --- a/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterMetadata.java +++ b/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterMetadata.java @@ -2,8 +2,8 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.util.Clock; @@ -11,6 +11,7 @@ import java.util.Optional; import java.util.OptionalLong; +@ProtobufMessage public record NewsletterMetadata( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) Optional name, @@ -28,7 +29,7 @@ public record NewsletterMetadata( Optional verification, @ProtobufProperty(index = 8, type = ProtobufType.UINT64) OptionalLong creationTimestampSeconds -) implements ProtobufMessage { +) { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) NewsletterMetadata( NewsletterName name, diff --git a/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterName.java b/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterName.java index 3f3c3acc5..356d67a51 100644 --- a/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterName.java +++ b/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterName.java @@ -1,14 +1,15 @@ package it.auties.whatsapp.model.newsletter; import com.fasterxml.jackson.annotation.JsonProperty; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.util.Clock; import java.time.ZonedDateTime; import java.util.Optional; +@ProtobufMessage public record NewsletterName( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String id, @@ -17,7 +18,7 @@ public record NewsletterName( @ProtobufProperty(index = 3, type = ProtobufType.UINT64) @JsonProperty("update_time") long updateTimeSeconds -) implements ProtobufMessage { +) { public Optional updateTime() { return Clock.parseSeconds(updateTimeSeconds); } diff --git a/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterPicture.java b/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterPicture.java index ce1c75774..f0b77548e 100644 --- a/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterPicture.java +++ b/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterPicture.java @@ -1,10 +1,11 @@ package it.auties.whatsapp.model.newsletter; import com.fasterxml.jackson.annotation.JsonProperty; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; +@ProtobufMessage public record NewsletterPicture( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String id, @@ -12,6 +13,6 @@ public record NewsletterPicture( String type, @ProtobufProperty(index = 3, type = ProtobufType.STRING) @JsonProperty("direct_path") String directPath -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterReaction.java b/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterReaction.java index 8d27c601f..681429f61 100644 --- a/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterReaction.java +++ b/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterReaction.java @@ -1,12 +1,13 @@ package it.auties.whatsapp.model.newsletter; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.Objects; -public final class NewsletterReaction implements ProtobufMessage { +@ProtobufMessage +public final class NewsletterReaction { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private final String content; @ProtobufProperty(index = 2, type = ProtobufType.UINT64) diff --git a/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterReactionSettings.java b/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterReactionSettings.java index 75cb20015..ecf112925 100644 --- a/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterReactionSettings.java +++ b/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterReactionSettings.java @@ -2,15 +2,16 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.util.Clock; import java.util.*; +@ProtobufMessage public record NewsletterReactionSettings( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) Type value, @@ -18,7 +19,7 @@ public record NewsletterReactionSettings( List blockedCodes, @ProtobufProperty(index = 3, type = ProtobufType.UINT64) OptionalLong enabledTimestampSeconds -) implements ProtobufMessage { +) { @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) public NewsletterReactionSettings(Type value, @JsonProperty("blocked_codes") List blockedCodes, @JsonProperty("enabled_ts_sec") Long enabledTimestampSeconds) { this( @@ -28,7 +29,8 @@ public NewsletterReactionSettings(Type value, @JsonProperty("blocked_codes") Lis ); } - public enum Type implements ProtobufEnum { + @ProtobufEnum + public enum Type { UNKNOWN(0), ALL(1), BASIC(2), diff --git a/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterSettings.java b/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterSettings.java index b1659b500..64a00ae2a 100644 --- a/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterSettings.java +++ b/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterSettings.java @@ -1,14 +1,15 @@ package it.auties.whatsapp.model.newsletter; import com.fasterxml.jackson.annotation.JsonProperty; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; +@ProtobufMessage public record NewsletterSettings( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) @JsonProperty("reaction_codes") NewsletterReactionSettings reactionCodes -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterState.java b/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterState.java index 0bbe64ed9..d7e40602f 100644 --- a/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterState.java +++ b/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterState.java @@ -1,13 +1,14 @@ package it.auties.whatsapp.model.newsletter; import com.fasterxml.jackson.annotation.JsonCreator; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.Objects; -public final class NewsletterState implements ProtobufMessage { +@ProtobufMessage +public final class NewsletterState { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private String type; diff --git a/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterViewerMetadata.java b/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterViewerMetadata.java index 03a28acd3..d4c829149 100644 --- a/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterViewerMetadata.java +++ b/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterViewerMetadata.java @@ -1,14 +1,15 @@ package it.auties.whatsapp.model.newsletter; import com.fasterxml.jackson.annotation.JsonCreator; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.Map; import java.util.Objects; -public final class NewsletterViewerMetadata implements ProtobufMessage { +@ProtobufMessage +public final class NewsletterViewerMetadata { @ProtobufProperty(index = 1, type = ProtobufType.BOOL) private boolean mute; @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) diff --git a/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterViewerRole.java b/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterViewerRole.java index 60638f16a..bb3413c15 100644 --- a/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterViewerRole.java +++ b/src/main/java/it/auties/whatsapp/model/newsletter/NewsletterViewerRole.java @@ -1,12 +1,13 @@ package it.auties.whatsapp.model.newsletter; import com.fasterxml.jackson.annotation.JsonValue; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; import java.util.Arrays; -public enum NewsletterViewerRole implements ProtobufEnum { +@ProtobufEnum +public enum NewsletterViewerRole { UNKNOWN(0), OWNER(1), SUBSCRIBER(2), @@ -14,6 +15,7 @@ public enum NewsletterViewerRole implements ProtobufEnum { GUEST(4); final int index; + NewsletterViewerRole(@ProtobufEnumIndex int index) { this.index = index; } diff --git a/src/main/java/it/auties/whatsapp/model/node/Attributes.java b/src/main/java/it/auties/whatsapp/model/node/Attributes.java index 7f4b20ece..1ef9d9aaf 100644 --- a/src/main/java/it/auties/whatsapp/model/node/Attributes.java +++ b/src/main/java/it/auties/whatsapp/model/node/Attributes.java @@ -25,7 +25,7 @@ public record Attributes(@JsonValue LinkedHashMap toMap) { * @return a new instance of Attributes */ @SafeVarargs - public static Attributes of(Entry... entries) { + public static Attributes of(Entry... entries) { return ofNullable(ofEntries(entries)); } @@ -37,7 +37,7 @@ public static Attributes of(Entry... entries) { */ @SafeVarargs @JsonCreator - public static Attributes ofNullable(Entry... entries) { + public static Attributes ofNullable(Entry... entries) { return entries == null ? of() : ofNullable(ofEntries(entries)); } @@ -401,7 +401,7 @@ public Attributes putAll(Map map) { } public Attributes putAll(Collection> entries) { - for(var entry : entries) { + for (var entry : entries) { toMap.put(entry.getKey(), entry.getValue()); } @@ -410,7 +410,7 @@ public Attributes putAll(Collection> entries) { @SafeVarargs public final Attributes putAll(Entry... entries) { - for(var entry : entries) { + for (var entry : entries) { toMap.put(entry.getKey(), entry.getValue()); } diff --git a/src/main/java/it/auties/whatsapp/model/payment/PaymentBackground.java b/src/main/java/it/auties/whatsapp/model/payment/PaymentBackground.java index 81f5cb495..2cc586cc5 100644 --- a/src/main/java/it/auties/whatsapp/model/payment/PaymentBackground.java +++ b/src/main/java/it/auties/whatsapp/model/payment/PaymentBackground.java @@ -1,15 +1,14 @@ package it.auties.whatsapp.model.payment; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.Optional; -@ProtobufMessageName("PaymentBackground") +@ProtobufMessage(name = "PaymentBackground") public record PaymentBackground( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String id, @@ -31,9 +30,10 @@ public record PaymentBackground( Optional mediaData, @ProtobufProperty(index = 10, type = ProtobufType.OBJECT) PaymentBackgroundType type -) implements ProtobufMessage { +) { - public enum PaymentBackgroundType implements ProtobufEnum { + @ProtobufEnum + public enum PaymentBackgroundType { UNKNOWN(0), DEFAULT(1); diff --git a/src/main/java/it/auties/whatsapp/model/payment/PaymentMediaData.java b/src/main/java/it/auties/whatsapp/model/payment/PaymentMediaData.java index f7b4bb15b..43b5c7a31 100644 --- a/src/main/java/it/auties/whatsapp/model/payment/PaymentMediaData.java +++ b/src/main/java/it/auties/whatsapp/model/payment/PaymentMediaData.java @@ -1,12 +1,11 @@ package it.auties.whatsapp.model.payment; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("PaymentBackground.MediaData") +@ProtobufMessage(name = "PaymentBackground.MediaData") public record PaymentMediaData( @ProtobufProperty(index = 1, type = ProtobufType.BYTES) byte[] mediaKey, @@ -18,6 +17,6 @@ public record PaymentMediaData( byte[] mediaEncryptedSha256, @ProtobufProperty(index = 5, type = ProtobufType.STRING) String mediaDirectPath -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/payment/PaymentMoney.java b/src/main/java/it/auties/whatsapp/model/payment/PaymentMoney.java index 74fa979cc..cd668587a 100644 --- a/src/main/java/it/auties/whatsapp/model/payment/PaymentMoney.java +++ b/src/main/java/it/auties/whatsapp/model/payment/PaymentMoney.java @@ -1,11 +1,10 @@ package it.auties.whatsapp.model.payment; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("Money") +@ProtobufMessage(name = "Money") public record PaymentMoney( @ProtobufProperty(index = 1, type = ProtobufType.INT64) long money, @@ -13,6 +12,6 @@ public record PaymentMoney( int offset, @ProtobufProperty(index = 3, type = ProtobufType.STRING) String currencyCode -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/poll/PollAdditionalMetadata.java b/src/main/java/it/auties/whatsapp/model/poll/PollAdditionalMetadata.java index 4ae7e3f99..1313552d1 100644 --- a/src/main/java/it/auties/whatsapp/model/poll/PollAdditionalMetadata.java +++ b/src/main/java/it/auties/whatsapp/model/poll/PollAdditionalMetadata.java @@ -1,17 +1,16 @@ package it.auties.whatsapp.model.poll; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; /** * A model class that represents additional metadata about a {@link it.auties.whatsapp.model.message.standard.PollCreationMessage} */ -@ProtobufMessageName("PollAdditionalMetadata") +@ProtobufMessage(name = "PollAdditionalMetadata") public record PollAdditionalMetadata( @ProtobufProperty(index = 1, type = ProtobufType.BOOL) boolean pollInvalidated -) implements ProtobufMessage { +) { } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/poll/PollOption.java b/src/main/java/it/auties/whatsapp/model/poll/PollOption.java index 8e9c438cd..8d1d0f1b5 100644 --- a/src/main/java/it/auties/whatsapp/model/poll/PollOption.java +++ b/src/main/java/it/auties/whatsapp/model/poll/PollOption.java @@ -1,18 +1,17 @@ package it.auties.whatsapp.model.poll; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; /** * A model class that represents an option in a * {@link it.auties.whatsapp.model.message.standard.PollCreationMessage} */ -@ProtobufMessageName("MsgOpaqueData.PollOption") +@ProtobufMessage(name = "MsgOpaqueData.PollOption") public record PollOption( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String name -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/poll/PollUpdate.java b/src/main/java/it/auties/whatsapp/model/poll/PollUpdate.java index e9bc4d17a..8bf6fa68f 100644 --- a/src/main/java/it/auties/whatsapp/model/poll/PollUpdate.java +++ b/src/main/java/it/auties/whatsapp/model/poll/PollUpdate.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.poll; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.message.model.ChatMessageKey; import it.auties.whatsapp.util.Clock; @@ -15,7 +14,7 @@ * {@link it.auties.whatsapp.model.message.standard.PollUpdateMessage} Not currently used, so it's * package private */ -@ProtobufMessageName("PollUpdate") +@ProtobufMessage(name = "PollUpdate") public record PollUpdate( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) ChatMessageKey pollUpdateMessageKey, @@ -23,7 +22,7 @@ public record PollUpdate( PollUpdateEncryptedOptions vote, @ProtobufProperty(index = 3, type = ProtobufType.INT64) long senderTimestampMilliseconds -) implements ProtobufMessage { +) { /** * Returns when the update was sent * diff --git a/src/main/java/it/auties/whatsapp/model/poll/PollUpdateEncryptedMetadata.java b/src/main/java/it/auties/whatsapp/model/poll/PollUpdateEncryptedMetadata.java index 3bf234fde..53351e4bb 100644 --- a/src/main/java/it/auties/whatsapp/model/poll/PollUpdateEncryptedMetadata.java +++ b/src/main/java/it/auties/whatsapp/model/poll/PollUpdateEncryptedMetadata.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.poll; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; @@ -10,12 +9,12 @@ * A model class that represents the cypher data to decode a * {@link it.auties.whatsapp.model.message.standard.PollUpdateMessage} */ -@ProtobufMessageName("PollEncValue") +@ProtobufMessage(name = "PollEncValue") public record PollUpdateEncryptedMetadata( @ProtobufProperty(index = 1, type = ProtobufType.BYTES) byte[] payload, @ProtobufProperty(index = 2, type = ProtobufType.BYTES) byte[] iv -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/poll/PollUpdateEncryptedOptions.java b/src/main/java/it/auties/whatsapp/model/poll/PollUpdateEncryptedOptions.java index 24f53826a..d6a0a72ba 100644 --- a/src/main/java/it/auties/whatsapp/model/poll/PollUpdateEncryptedOptions.java +++ b/src/main/java/it/auties/whatsapp/model/poll/PollUpdateEncryptedOptions.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.poll; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.List; @@ -10,10 +9,10 @@ /** * A model class that represents the cypher data to decode the votes of a user inside {@link it.auties.whatsapp.model.message.standard.PollUpdateMessage} */ -@ProtobufMessageName("Message.PollVoteMessage") +@ProtobufMessage(name = "Message.PollVoteMessage") public record PollUpdateEncryptedOptions( @ProtobufProperty(index = 1, type = ProtobufType.BYTES) List selectedOptions -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/poll/PollUpdateMessageMetadata.java b/src/main/java/it/auties/whatsapp/model/poll/PollUpdateMessageMetadata.java index 8d924a678..de369e50b 100644 --- a/src/main/java/it/auties/whatsapp/model/poll/PollUpdateMessageMetadata.java +++ b/src/main/java/it/auties/whatsapp/model/poll/PollUpdateMessageMetadata.java @@ -1,15 +1,14 @@ package it.auties.whatsapp.model.poll; -import it.auties.protobuf.annotation.ProtobufMessageName; -import it.auties.protobuf.model.ProtobufMessage; +import it.auties.protobuf.annotation.ProtobufMessage; /** * A model class that represents additional metadata about a * {@link it.auties.whatsapp.model.message.standard.PollUpdateMessage} Currently empty */ -@ProtobufMessageName("Message.PollUpdateMessageMetadata") +@ProtobufMessage(name = "Message.PollUpdateMessageMetadata") public record PollUpdateMessageMetadata( -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/poll/SelectedPollOption.java b/src/main/java/it/auties/whatsapp/model/poll/SelectedPollOption.java index 2c602eee8..c727473aa 100644 --- a/src/main/java/it/auties/whatsapp/model/poll/SelectedPollOption.java +++ b/src/main/java/it/auties/whatsapp/model/poll/SelectedPollOption.java @@ -1,18 +1,19 @@ package it.auties.whatsapp.model.poll; +import it.auties.protobuf.annotation.ProtobufMessage; 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; /** * A model class that represents a selected option in a {@link it.auties.whatsapp.model.message.standard.PollCreationMessage} */ +@ProtobufMessage public record SelectedPollOption( @ProtobufProperty(index = 1, type = ProtobufType.STRING) Jid jid, @ProtobufProperty(index = 2, type = ProtobufType.STRING) String name -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/privacy/PrivacySettingEntry.java b/src/main/java/it/auties/whatsapp/model/privacy/PrivacySettingEntry.java index 5f4576eef..c2b57b62f 100644 --- a/src/main/java/it/auties/whatsapp/model/privacy/PrivacySettingEntry.java +++ b/src/main/java/it/auties/whatsapp/model/privacy/PrivacySettingEntry.java @@ -1,7 +1,7 @@ package it.auties.whatsapp.model.privacy; +import it.auties.protobuf.annotation.ProtobufMessage; 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; @@ -14,6 +14,7 @@ * @param value the non-null value * @param excluded the non-null list of excluded contacts if {@link PrivacySettingEntry#value} == {@link PrivacySettingValue#CONTACTS_EXCEPT} */ +@ProtobufMessage public record PrivacySettingEntry( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) PrivacySettingType type, @@ -21,7 +22,7 @@ public record PrivacySettingEntry( PrivacySettingValue value, @ProtobufProperty(index = 3, type = ProtobufType.STRING) List excluded -) implements ProtobufMessage { +) { /** * Canonical constructor */ diff --git a/src/main/java/it/auties/whatsapp/model/privacy/PrivacySettingType.java b/src/main/java/it/auties/whatsapp/model/privacy/PrivacySettingType.java index 1908d15c4..983024226 100644 --- a/src/main/java/it/auties/whatsapp/model/privacy/PrivacySettingType.java +++ b/src/main/java/it/auties/whatsapp/model/privacy/PrivacySettingType.java @@ -1,8 +1,8 @@ package it.auties.whatsapp.model.privacy; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; import java.util.*; @@ -10,7 +10,8 @@ * The constants of this enumerated type describe the various types of settings that a user can * toggle in his account's preferences */ -public enum PrivacySettingType implements ProtobufEnum { +@ProtobufEnum +public enum PrivacySettingType { /** * Refers to whether your last access on Whatsapp should be visible */ diff --git a/src/main/java/it/auties/whatsapp/model/privacy/PrivacySettingValue.java b/src/main/java/it/auties/whatsapp/model/privacy/PrivacySettingValue.java index 419c7c601..de89fa666 100644 --- a/src/main/java/it/auties/whatsapp/model/privacy/PrivacySettingValue.java +++ b/src/main/java/it/auties/whatsapp/model/privacy/PrivacySettingValue.java @@ -1,7 +1,7 @@ package it.auties.whatsapp.model.privacy; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; import java.util.Arrays; import java.util.Objects; @@ -11,7 +11,8 @@ * The constants of this enumerated type describe the various types of preferences that can be * toggled for a corresponding setting */ -public enum PrivacySettingValue implements ProtobufEnum { +@ProtobufEnum +public enum PrivacySettingValue { /** * Everyone */ diff --git a/src/main/java/it/auties/whatsapp/model/product/Product.java b/src/main/java/it/auties/whatsapp/model/product/Product.java index 2e03ef150..127ff2ce6 100644 --- a/src/main/java/it/auties/whatsapp/model/product/Product.java +++ b/src/main/java/it/auties/whatsapp/model/product/Product.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.product; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.message.standard.ImageMessage; @@ -10,7 +9,7 @@ /** * A model class that represents a product */ -@ProtobufMessageName("Message.ListMessage.Product") +@ProtobufMessage(name = "Message.ListMessage.Product") public record Product( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) ImageMessage image, @@ -34,6 +33,6 @@ public record Product( String firstImageId, @ProtobufProperty(index = 12, type = ProtobufType.INT64) long salePriceAmount1000 -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/product/ProductCatalog.java b/src/main/java/it/auties/whatsapp/model/product/ProductCatalog.java index e08a63794..a437b3c3d 100644 --- a/src/main/java/it/auties/whatsapp/model/product/ProductCatalog.java +++ b/src/main/java/it/auties/whatsapp/model/product/ProductCatalog.java @@ -1,15 +1,14 @@ package it.auties.whatsapp.model.product; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.message.standard.ImageMessage; /** * A model class that represents a product catalog */ -@ProtobufMessageName("Message.ProductMessage.CatalogSnapshot") +@ProtobufMessage(name = "Message.ProductMessage.CatalogSnapshot") public record ProductCatalog( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) ImageMessage catalogImage, @@ -17,6 +16,6 @@ public record ProductCatalog( String title, @ProtobufProperty(index = 3, type = ProtobufType.STRING) String description -) implements ProtobufMessage { +) { } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/product/ProductListHeaderImage.java b/src/main/java/it/auties/whatsapp/model/product/ProductListHeaderImage.java index 52e66ce55..32d33ada7 100644 --- a/src/main/java/it/auties/whatsapp/model/product/ProductListHeaderImage.java +++ b/src/main/java/it/auties/whatsapp/model/product/ProductListHeaderImage.java @@ -1,19 +1,18 @@ package it.auties.whatsapp.model.product; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; /** * A model class that represents the header of a product list */ -@ProtobufMessageName("Message.ListMessage.ProductListHeaderImage") +@ProtobufMessage(name = "Message.ListMessage.ProductListHeaderImage") public record ProductListHeaderImage( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String id, @ProtobufProperty(index = 2, type = ProtobufType.BYTES) byte[] thumbnail -) implements ProtobufMessage { +) { } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/product/ProductSection.java b/src/main/java/it/auties/whatsapp/model/product/ProductSection.java index e14a45732..b639a3d68 100644 --- a/src/main/java/it/auties/whatsapp/model/product/ProductSection.java +++ b/src/main/java/it/auties/whatsapp/model/product/ProductSection.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.product; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.List; @@ -10,12 +9,12 @@ /** * A model class that represents a section inside a list of products */ -@ProtobufMessageName("Message.ListMessage.ProductSection") +@ProtobufMessage(name = "Message.ListMessage.ProductSection") public record ProductSection( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String title, @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) List products -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/product/ProductSectionEntry.java b/src/main/java/it/auties/whatsapp/model/product/ProductSectionEntry.java index ea22ce38c..986225a81 100644 --- a/src/main/java/it/auties/whatsapp/model/product/ProductSectionEntry.java +++ b/src/main/java/it/auties/whatsapp/model/product/ProductSectionEntry.java @@ -1,17 +1,16 @@ package it.auties.whatsapp.model.product; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; /** * A model class that represents a product */ -@ProtobufMessageName("Message.ListMessage.Product") +@ProtobufMessage(name = "Message.ListMessage.Product") public record ProductSectionEntry( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String id -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/request/QueryNewsletterRequest.java b/src/main/java/it/auties/whatsapp/model/request/QueryNewsletterRequest.java index adfb1ce4b..66a9b4f07 100644 --- a/src/main/java/it/auties/whatsapp/model/request/QueryNewsletterRequest.java +++ b/src/main/java/it/auties/whatsapp/model/request/QueryNewsletterRequest.java @@ -5,7 +5,9 @@ import it.auties.whatsapp.model.newsletter.NewsletterViewerRole; public record QueryNewsletterRequest(Variable variables) { - public record Variable(Input input, @JsonProperty("fetch_viewer_metadata") boolean fetchViewerMetadata, @JsonProperty("fetch_full_image") boolean fetchFullImage, @JsonProperty("fetch_creation_time") boolean fetchCreationTime) { + public record Variable(Input input, @JsonProperty("fetch_viewer_metadata") boolean fetchViewerMetadata, + @JsonProperty("fetch_full_image") boolean fetchFullImage, + @JsonProperty("fetch_creation_time") boolean fetchCreationTime) { } diff --git a/src/main/java/it/auties/whatsapp/model/response/AcceptAdminInviteNewsletterResponse.java b/src/main/java/it/auties/whatsapp/model/response/AcceptAdminInviteNewsletterResponse.java index 315d24e3b..813fd20ab 100644 --- a/src/main/java/it/auties/whatsapp/model/response/AcceptAdminInviteNewsletterResponse.java +++ b/src/main/java/it/auties/whatsapp/model/response/AcceptAdminInviteNewsletterResponse.java @@ -24,7 +24,8 @@ private record JsonData(Optional data) { } - private record JsonResponse(@JsonProperty("xwa2_newsletter_admin_invite_accept") AcceptAdminInviteNewsletterResponse response) { + private record JsonResponse( + @JsonProperty("xwa2_newsletter_admin_invite_accept") AcceptAdminInviteNewsletterResponse response) { } } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/response/ContactAboutResponse.java b/src/main/java/it/auties/whatsapp/model/response/ContactAboutResponse.java index 963fb6abe..1253fa2e0 100644 --- a/src/main/java/it/auties/whatsapp/model/response/ContactAboutResponse.java +++ b/src/main/java/it/auties/whatsapp/model/response/ContactAboutResponse.java @@ -21,7 +21,8 @@ public static ContactAboutResponse ofNode(Node source) { @SuppressWarnings("unchecked") public static Optional ofJson(String json) { try { - var parsedJson = Json.readValue(json, new TypeReference>() {}); + var parsedJson = Json.readValue(json, new TypeReference>() { + }); var data = (Map) parsedJson.get("data"); var updates = (List) data.get("xwa2_users_updates_since"); var latestUpdate = (Map) updates.getFirst(); diff --git a/src/main/java/it/auties/whatsapp/model/response/CreateAdminInviteNewsletterResponse.java b/src/main/java/it/auties/whatsapp/model/response/CreateAdminInviteNewsletterResponse.java index 11920408d..aa3bd415a 100644 --- a/src/main/java/it/auties/whatsapp/model/response/CreateAdminInviteNewsletterResponse.java +++ b/src/main/java/it/auties/whatsapp/model/response/CreateAdminInviteNewsletterResponse.java @@ -24,7 +24,8 @@ private record JsonData(Optional data) { } - private record JsonResponse(@JsonProperty("xwa2_newsletter_admin_invite_create") CreateAdminInviteNewsletterResponse response) { + private record JsonResponse( + @JsonProperty("xwa2_newsletter_admin_invite_create") CreateAdminInviteNewsletterResponse response) { } } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/response/NewsletterMuteResponse.java b/src/main/java/it/auties/whatsapp/model/response/NewsletterMuteResponse.java index f5d99283e..5990856a7 100644 --- a/src/main/java/it/auties/whatsapp/model/response/NewsletterMuteResponse.java +++ b/src/main/java/it/auties/whatsapp/model/response/NewsletterMuteResponse.java @@ -25,7 +25,8 @@ private record JsonData(Optional data) { } - private record JsonResponse(@JsonProperty("xwa2_notify_newsletter_on_mute_change") NewsletterMuteResponse response) { + private record JsonResponse( + @JsonProperty("xwa2_notify_newsletter_on_mute_change") NewsletterMuteResponse response) { } } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/response/NewsletterStateResponse.java b/src/main/java/it/auties/whatsapp/model/response/NewsletterStateResponse.java index 2764fb473..7b2d96024 100644 --- a/src/main/java/it/auties/whatsapp/model/response/NewsletterStateResponse.java +++ b/src/main/java/it/auties/whatsapp/model/response/NewsletterStateResponse.java @@ -7,7 +7,8 @@ import java.util.Optional; -public record NewsletterStateResponse(@JsonProperty("id") Jid jid, @JsonProperty("is_requestor") boolean isRequestor, NewsletterState state) { +public record NewsletterStateResponse(@JsonProperty("id") Jid jid, @JsonProperty("is_requestor") boolean isRequestor, + NewsletterState state) { public static Optional ofJson(String json) { return Json.readValue(json, JsonResponse.class) .data() diff --git a/src/main/java/it/auties/whatsapp/model/response/RegistrationResponse.java b/src/main/java/it/auties/whatsapp/model/response/RegistrationResponse.java index a38169170..294417d2b 100644 --- a/src/main/java/it/auties/whatsapp/model/response/RegistrationResponse.java +++ b/src/main/java/it/auties/whatsapp/model/response/RegistrationResponse.java @@ -9,23 +9,23 @@ /** * A model that represents a newsletters from Whatsapp regarding the registration of a phone number * - * @param number the number that was registered - * @param lid the lid of the number that was registered - * @param status the status of the registration - * @param errorReason the error, if any was thrown - * @param method the method used to register, if any was used - * @param codeLength the expected length of the code, if a code request was sent - * @param notifyAfter the time in seconds after which the app would notify you to try again to register - * @param retryAfter the time in seconds after which the app would allow you to try again to register a sms - * @param voiceLength unknown - * @param callWait the time in seconds after which the app would allow you to try again to register using a call - * @param smsWait the time in seconds after which the app would allow you to try again to register using a sms - * @param flashType unknown - * @param whatsappWait the last wait time in seconds before trying again, if available - * @param securityCodeSet whether 2fa is enabled - * @param imageCaptcha the image captcha to solve, only available for business accounts - * @param audioCaptcha the audio captcha to solve, only available for business accounts - * @param whatsappOldEligible if requested, whether the phone number was already registered on Whatsapp + * @param number the number that was registered + * @param lid the lid of the number that was registered + * @param status the status of the registration + * @param errorReason the error, if any was thrown + * @param method the method used to register, if any was used + * @param codeLength the expected length of the code, if a code request was sent + * @param notifyAfter the time in seconds after which the app would notify you to try again to register + * @param retryAfter the time in seconds after which the app would allow you to try again to register a sms + * @param voiceLength unknown + * @param callWait the time in seconds after which the app would allow you to try again to register using a call + * @param smsWait the time in seconds after which the app would allow you to try again to register using a sms + * @param flashType unknown + * @param whatsappWait the last wait time in seconds before trying again, if available + * @param securityCodeSet whether 2fa is enabled + * @param imageCaptcha the image captcha to solve, only available for business accounts + * @param audioCaptcha the audio captcha to solve, only available for business accounts + * @param whatsappOldEligible if requested, whether the phone number was already registered on Whatsapp */ public record RegistrationResponse(@JsonProperty("login") PhoneNumber number, @JsonProperty("lid") long lid, @@ -43,6 +43,7 @@ public record RegistrationResponse(@JsonProperty("login") PhoneNumber number, @JsonProperty("security_code_set") boolean securityCodeSet, @JsonProperty("image_blob") String imageCaptcha, @JsonProperty("audio_blob") String audioCaptcha, + @JsonProperty("cert") String cert, @JsonProperty("wa_old_eligible") boolean whatsappOldEligible, @JsonProperty("possible_migration") boolean possibleMigration, @JsonProperty(value = "autoconf_type", defaultValue = "0") boolean autoConfigure, diff --git a/src/main/java/it/auties/whatsapp/model/response/RevokeAdminInviteNewsletterResponse.java b/src/main/java/it/auties/whatsapp/model/response/RevokeAdminInviteNewsletterResponse.java index d6759f2ab..b485f902c 100644 --- a/src/main/java/it/auties/whatsapp/model/response/RevokeAdminInviteNewsletterResponse.java +++ b/src/main/java/it/auties/whatsapp/model/response/RevokeAdminInviteNewsletterResponse.java @@ -24,7 +24,8 @@ private record JsonData(Optional data) { } - private record JsonResponse(@JsonProperty("xwa2_newsletter_admin_invite_revoke") RevokeAdminInviteNewsletterResponse response) { + private record JsonResponse( + @JsonProperty("xwa2_newsletter_admin_invite_revoke") RevokeAdminInviteNewsletterResponse response) { } } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/response/UserChosenNameResponse.java b/src/main/java/it/auties/whatsapp/model/response/UserChosenNameResponse.java index ff56036bd..d9959e414 100644 --- a/src/main/java/it/auties/whatsapp/model/response/UserChosenNameResponse.java +++ b/src/main/java/it/auties/whatsapp/model/response/UserChosenNameResponse.java @@ -12,7 +12,8 @@ public record UserChosenNameResponse(Optional name) { @SuppressWarnings("unchecked") public static Optional ofJson(String json) { try { - var parsedJson = Json.readValue(json, new TypeReference>() {}); + var parsedJson = Json.readValue(json, new TypeReference>() { + }); var data = (Map) parsedJson.get("data"); var updates = (List) data.get("xwa2_users_updates_since"); var latestUpdate = (Map) updates.getFirst(); diff --git a/src/main/java/it/auties/whatsapp/model/setting/AutoDownloadSettings.java b/src/main/java/it/auties/whatsapp/model/setting/AutoDownloadSettings.java index 159d9a9eb..f383e46df 100644 --- a/src/main/java/it/auties/whatsapp/model/setting/AutoDownloadSettings.java +++ b/src/main/java/it/auties/whatsapp/model/setting/AutoDownloadSettings.java @@ -1,10 +1,10 @@ package it.auties.whatsapp.model.setting; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("AutoDownloadSettings") +@ProtobufMessage(name = "AutoDownloadSettings") public record AutoDownloadSettings( @ProtobufProperty(index = 1, type = ProtobufType.BOOL) boolean downloadImages, diff --git a/src/main/java/it/auties/whatsapp/model/setting/AvatarUserSettings.java b/src/main/java/it/auties/whatsapp/model/setting/AvatarUserSettings.java index 3373a5758..dc0440d39 100644 --- a/src/main/java/it/auties/whatsapp/model/setting/AvatarUserSettings.java +++ b/src/main/java/it/auties/whatsapp/model/setting/AvatarUserSettings.java @@ -1,10 +1,10 @@ package it.auties.whatsapp.model.setting; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("AvatarUserSettings") +@ProtobufMessage(name = "AvatarUserSettings") public record AvatarUserSettings( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String facebookId, diff --git a/src/main/java/it/auties/whatsapp/model/setting/EphemeralSettings.java b/src/main/java/it/auties/whatsapp/model/setting/EphemeralSettings.java index 37916b938..34b8abd79 100644 --- a/src/main/java/it/auties/whatsapp/model/setting/EphemeralSettings.java +++ b/src/main/java/it/auties/whatsapp/model/setting/EphemeralSettings.java @@ -1,6 +1,6 @@ package it.auties.whatsapp.model.setting; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.util.Clock; @@ -8,7 +8,7 @@ import java.time.ZonedDateTime; import java.util.Optional; -@ProtobufMessageName("AvatarUserSetting") +@ProtobufMessage(name = "AvatarUserSetting") public record EphemeralSettings( @ProtobufProperty(index = 1, type = ProtobufType.SFIXED32) int duration, diff --git a/src/main/java/it/auties/whatsapp/model/setting/GlobalSettings.java b/src/main/java/it/auties/whatsapp/model/setting/GlobalSettings.java index 35e243687..90c46d638 100644 --- a/src/main/java/it/auties/whatsapp/model/setting/GlobalSettings.java +++ b/src/main/java/it/auties/whatsapp/model/setting/GlobalSettings.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.setting; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.chat.ChatWallpaper; import it.auties.whatsapp.model.media.MediaVisibility; @@ -12,7 +11,7 @@ import java.util.Optional; -@ProtobufMessageName("GlobalSettings") +@ProtobufMessage(name = "GlobalSettings") public record GlobalSettings( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) Optional lightThemeWallpaper, @@ -36,7 +35,7 @@ public record GlobalSettings( long disappearingModeTimestampSeconds, @ProtobufProperty(index = 11, type = ProtobufType.OBJECT) AvatarUserSettings avatarUserSettings -) implements ProtobufMessage { +) { /** * Returns when the disappearing mode was toggled * diff --git a/src/main/java/it/auties/whatsapp/model/setting/LocaleSettings.java b/src/main/java/it/auties/whatsapp/model/setting/LocaleSettings.java index 79e1b4d08..df6c7f759 100644 --- a/src/main/java/it/auties/whatsapp/model/setting/LocaleSettings.java +++ b/src/main/java/it/auties/whatsapp/model/setting/LocaleSettings.java @@ -1,10 +1,10 @@ package it.auties.whatsapp.model.setting; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("LocaleSetting") +@ProtobufMessage(name = "LocaleSetting") public record LocaleSettings( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String locale diff --git a/src/main/java/it/auties/whatsapp/model/setting/PushNameSettings.java b/src/main/java/it/auties/whatsapp/model/setting/PushNameSettings.java index 86d98f92e..5b41b26c7 100644 --- a/src/main/java/it/auties/whatsapp/model/setting/PushNameSettings.java +++ b/src/main/java/it/auties/whatsapp/model/setting/PushNameSettings.java @@ -1,10 +1,10 @@ package it.auties.whatsapp.model.setting; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("SyncActionValue.PushNameSetting") +@ProtobufMessage(name = "SyncActionValue.PushNameSetting") public record PushNameSettings( @ProtobufProperty(index = 1, type = ProtobufType.STRING) String name diff --git a/src/main/java/it/auties/whatsapp/model/setting/SecurityNotificationSettings.java b/src/main/java/it/auties/whatsapp/model/setting/SecurityNotificationSettings.java index 13b3b7b1e..e78710bae 100644 --- a/src/main/java/it/auties/whatsapp/model/setting/SecurityNotificationSettings.java +++ b/src/main/java/it/auties/whatsapp/model/setting/SecurityNotificationSettings.java @@ -1,10 +1,10 @@ package it.auties.whatsapp.model.setting; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("SyncActionValue.SecurityNotificationSetting") +@ProtobufMessage(name = "SyncActionValue.SecurityNotificationSetting") public record SecurityNotificationSettings( @ProtobufProperty(index = 1, type = ProtobufType.BOOL) boolean showNotification diff --git a/src/main/java/it/auties/whatsapp/model/setting/Setting.java b/src/main/java/it/auties/whatsapp/model/setting/Setting.java index fd6b76897..4de88b9be 100644 --- a/src/main/java/it/auties/whatsapp/model/setting/Setting.java +++ b/src/main/java/it/auties/whatsapp/model/setting/Setting.java @@ -1,7 +1,5 @@ package it.auties.whatsapp.model.setting; -import it.auties.protobuf.model.ProtobufMessage; - -public sealed interface Setting extends ProtobufMessage permits AutoDownloadSettings, AvatarUserSettings, EphemeralSettings, LocaleSettings, PushNameSettings, SecurityNotificationSettings, UnarchiveChatsSettings { +public sealed interface Setting permits AutoDownloadSettings, AvatarUserSettings, EphemeralSettings, LocaleSettings, PushNameSettings, SecurityNotificationSettings, UnarchiveChatsSettings { String indexName(); } diff --git a/src/main/java/it/auties/whatsapp/model/setting/UnarchiveChatsSettings.java b/src/main/java/it/auties/whatsapp/model/setting/UnarchiveChatsSettings.java index ee946e8ed..acf38e3b9 100644 --- a/src/main/java/it/auties/whatsapp/model/setting/UnarchiveChatsSettings.java +++ b/src/main/java/it/auties/whatsapp/model/setting/UnarchiveChatsSettings.java @@ -1,10 +1,10 @@ package it.auties.whatsapp.model.setting; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("SyncActionValue.UnarchiveChatsSetting") +@ProtobufMessage(name = "SyncActionValue.UnarchiveChatsSetting") public record UnarchiveChatsSettings( @ProtobufProperty(index = 1, type = ProtobufType.BOOL) boolean unarchiveChats diff --git a/src/main/java/it/auties/whatsapp/model/signal/auth/ClientFinish.java b/src/main/java/it/auties/whatsapp/model/signal/auth/ClientFinish.java index 7302aa6fd..80a824647 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/auth/ClientFinish.java +++ b/src/main/java/it/auties/whatsapp/model/signal/auth/ClientFinish.java @@ -1,12 +1,11 @@ package it.auties.whatsapp.model.signal.auth; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import static it.auties.protobuf.model.ProtobufType.BYTES; -@ProtobufMessageName("HandshakeMessage.ClientFinish") +@ProtobufMessage(name = "HandshakeMessage.ClientFinish") public record ClientFinish(@ProtobufProperty(index = 1, type = BYTES) byte[] _static, - @ProtobufProperty(index = 2, type = BYTES) byte[] payload) implements ProtobufMessage { + @ProtobufProperty(index = 2, type = BYTES) byte[] payload) { } diff --git a/src/main/java/it/auties/whatsapp/model/signal/auth/ClientHello.java b/src/main/java/it/auties/whatsapp/model/signal/auth/ClientHello.java index 0f970daa3..477800282 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/auth/ClientHello.java +++ b/src/main/java/it/auties/whatsapp/model/signal/auth/ClientHello.java @@ -1,13 +1,12 @@ package it.auties.whatsapp.model.signal.auth; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import static it.auties.protobuf.model.ProtobufType.BYTES; -@ProtobufMessageName("HandshakeMessage.ClientHello") +@ProtobufMessage(name = "HandshakeMessage.ClientHello") public record ClientHello(@ProtobufProperty(index = 1, type = BYTES) byte[] ephemeral, @ProtobufProperty(index = 2, type = BYTES) byte[] _static, - @ProtobufProperty(index = 3, type = BYTES) byte[] payload) implements ProtobufMessage { + @ProtobufProperty(index = 3, type = BYTES) byte[] payload) { } diff --git a/src/main/java/it/auties/whatsapp/model/signal/auth/ClientPayload.java b/src/main/java/it/auties/whatsapp/model/signal/auth/ClientPayload.java index c4d53d052..c084dbd32 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/auth/ClientPayload.java +++ b/src/main/java/it/auties/whatsapp/model/signal/auth/ClientPayload.java @@ -1,16 +1,15 @@ package it.auties.whatsapp.model.signal.auth; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import java.util.List; import static it.auties.protobuf.model.ProtobufType.*; -@ProtobufMessageName("ClientPayload") +@ProtobufMessage(name = "ClientPayload") public record ClientPayload(@ProtobufProperty(index = 1, type = UINT64) Long username, @ProtobufProperty(index = 3, type = BOOL) Boolean passive, @ProtobufProperty(index = 5, type = OBJECT) UserAgent userAgent, @@ -33,10 +32,10 @@ public record ClientPayload(@ProtobufProperty(index = 1, type = UINT64) Long use @ProtobufProperty(index = 30, type = OBJECT) ClientPayloadIOSAppExtension iosAppExtension, @ProtobufProperty(index = 31, type = UINT64) Long fbAppId, @ProtobufProperty(index = 32, type = BYTES) byte[] fbDeviceId, - @ProtobufProperty(index = 33, type = BOOL) Boolean pull) implements ProtobufMessage { - - public enum ClientPayloadConnectType implements ProtobufEnum { + @ProtobufProperty(index = 33, type = BOOL) Boolean pull) { + @ProtobufEnum + public enum ClientPayloadConnectType { CELLULAR_UNKNOWN(0), WIFI_UNKNOWN(1), CELLULAR_EDGE(100), @@ -64,8 +63,8 @@ public int index() { } } - public enum ClientPayloadConnectReason implements ProtobufEnum { - + @ProtobufEnum + public enum ClientPayloadConnectReason { PUSH(0), USER_ACTIVATED(1), SCHEDULED(2), @@ -84,8 +83,8 @@ public int index() { } } - public enum ClientPayloadProduct implements ProtobufEnum { - + @ProtobufEnum + public enum ClientPayloadProduct { WHATSAPP(0), MESSENGER(1); @@ -100,8 +99,8 @@ public int index() { } } - public enum ClientPayloadIOSAppExtension implements ProtobufEnum { - + @ProtobufEnum + public enum ClientPayloadIOSAppExtension { SHARE_EXTENSION(0), SERVICE_EXTENSION(1), INTENTS_EXTENSION(2); diff --git a/src/main/java/it/auties/whatsapp/model/signal/auth/CompanionProperties.java b/src/main/java/it/auties/whatsapp/model/signal/auth/CompanionProperties.java index 9f05c9fed..81d4d296c 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/auth/CompanionProperties.java +++ b/src/main/java/it/auties/whatsapp/model/signal/auth/CompanionProperties.java @@ -1,24 +1,23 @@ package it.auties.whatsapp.model.signal.auth; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.whatsapp.model.sync.HistorySyncConfig; import static it.auties.protobuf.model.ProtobufType.*; -@ProtobufMessageName("DeviceProps") +@ProtobufMessage(name = "DeviceProps") public record CompanionProperties(@ProtobufProperty(index = 1, type = STRING) String os, @ProtobufProperty(index = 2, type = OBJECT) Version version, @ProtobufProperty(index = 3, type = OBJECT) PlatformType platformType, @ProtobufProperty(index = 4, type = BOOL) boolean requireFullSync, - @ProtobufProperty(index = 5, type = OBJECT) HistorySyncConfig historySyncConfig) implements ProtobufMessage { - - public enum PlatformType implements ProtobufEnum { + @ProtobufProperty(index = 5, type = OBJECT) HistorySyncConfig historySyncConfig) { + @ProtobufEnum + public enum PlatformType { UNKNOWN(0), CHROME(1), FIREFOX(2), diff --git a/src/main/java/it/auties/whatsapp/model/signal/auth/CompanionRegistrationData.java b/src/main/java/it/auties/whatsapp/model/signal/auth/CompanionRegistrationData.java index 47be72983..6e462aa27 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/auth/CompanionRegistrationData.java +++ b/src/main/java/it/auties/whatsapp/model/signal/auth/CompanionRegistrationData.java @@ -1,11 +1,10 @@ package it.auties.whatsapp.model.signal.auth; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("ClientPayload.DevicePairingRegistrationData") +@ProtobufMessage(name = "ClientPayload.DevicePairingRegistrationData") public record CompanionRegistrationData(@ProtobufProperty(index = 1, type = ProtobufType.BYTES) byte[] eRegid, @ProtobufProperty(index = 2, type = ProtobufType.BYTES) byte[] eKeytype, @ProtobufProperty(index = 3, type = ProtobufType.BYTES) byte[] eIdent, @@ -13,5 +12,5 @@ public record CompanionRegistrationData(@ProtobufProperty(index = 1, type = Prot @ProtobufProperty(index = 5, type = ProtobufType.BYTES) byte[] eSkeyVal, @ProtobufProperty(index = 6, type = ProtobufType.BYTES) byte[] eSkeySig, @ProtobufProperty(index = 7, type = ProtobufType.BYTES) byte[] buildHash, - @ProtobufProperty(index = 8, type = ProtobufType.BYTES) byte[] companionProps) implements ProtobufMessage { + @ProtobufProperty(index = 8, type = ProtobufType.BYTES) byte[] companionProps) { } diff --git a/src/main/java/it/auties/whatsapp/model/signal/auth/DNSSource.java b/src/main/java/it/auties/whatsapp/model/signal/auth/DNSSource.java index c8eaed88d..b4573af6c 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/auth/DNSSource.java +++ b/src/main/java/it/auties/whatsapp/model/signal/auth/DNSSource.java @@ -1,21 +1,19 @@ package it.auties.whatsapp.model.signal.auth; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import static it.auties.protobuf.model.ProtobufType.BOOL; import static it.auties.protobuf.model.ProtobufType.OBJECT; -@ProtobufMessageName("ClientPayload.DNSSource") +@ProtobufMessage(name = "ClientPayload.DNSSource") public record DNSSource(@ProtobufProperty(index = 15, type = OBJECT) ResolutionMethod dnsMethod, - @ProtobufProperty(index = 16, type = BOOL) boolean appCached) implements ProtobufMessage { - - @ProtobufMessageName("ClientPayload.DNSSource.DNSResolutionMethod") - public enum ResolutionMethod implements ProtobufEnum { + @ProtobufProperty(index = 16, type = BOOL) boolean appCached) { + @ProtobufEnum(name = "ClientPayload.DNSSource.DNSResolutionMethod") + public enum ResolutionMethod { SYSTEM(0), GOOGLE(1), HARDCODED(2), diff --git a/src/main/java/it/auties/whatsapp/model/signal/auth/DeviceIdentity.java b/src/main/java/it/auties/whatsapp/model/signal/auth/DeviceIdentity.java index d7c557a9a..c0139fc89 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/auth/DeviceIdentity.java +++ b/src/main/java/it/auties/whatsapp/model/signal/auth/DeviceIdentity.java @@ -1,15 +1,14 @@ package it.auties.whatsapp.model.signal.auth; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("ADVDeviceIdentity") +@ProtobufMessage(name = "ADVDeviceIdentity") public record DeviceIdentity(@ProtobufProperty(index = 1, type = ProtobufType.UINT32) int rawId, @ProtobufProperty(index = 2, type = ProtobufType.UINT64) long timestamp, @ProtobufProperty(index = 3, type = ProtobufType.UINT32) - int keyIndex) implements ProtobufMessage { + int keyIndex) { } diff --git a/src/main/java/it/auties/whatsapp/model/signal/auth/HandshakeMessage.java b/src/main/java/it/auties/whatsapp/model/signal/auth/HandshakeMessage.java index d3e1d5bf7..9d083e53e 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/auth/HandshakeMessage.java +++ b/src/main/java/it/auties/whatsapp/model/signal/auth/HandshakeMessage.java @@ -1,13 +1,12 @@ package it.auties.whatsapp.model.signal.auth; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import static it.auties.protobuf.model.ProtobufType.OBJECT; -@ProtobufMessageName("HandshakeMessage") +@ProtobufMessage(name = "HandshakeMessage") public record HandshakeMessage(@ProtobufProperty(index = 2, type = OBJECT) ClientHello clientHello, @ProtobufProperty(index = 3, type = OBJECT) ServerHello serverHello, - @ProtobufProperty(index = 4, type = OBJECT) ClientFinish clientFinish) implements ProtobufMessage { + @ProtobufProperty(index = 4, type = OBJECT) ClientFinish clientFinish) { } diff --git a/src/main/java/it/auties/whatsapp/model/signal/auth/KeyIndexList.java b/src/main/java/it/auties/whatsapp/model/signal/auth/KeyIndexList.java index 063d18d5c..b9b5995b5 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/auth/KeyIndexList.java +++ b/src/main/java/it/auties/whatsapp/model/signal/auth/KeyIndexList.java @@ -1,15 +1,14 @@ package it.auties.whatsapp.model.signal.auth; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.List; -@ProtobufMessageName("ADVKeyIndexList") +@ProtobufMessage(name = "ADVKeyIndexList") public record KeyIndexList(@ProtobufProperty(index = 1, type = ProtobufType.UINT32) int rawId, @ProtobufProperty(index = 2, type = ProtobufType.UINT64) long timestamp, @ProtobufProperty(index = 3, type = ProtobufType.UINT32) int currentIndex, - @ProtobufProperty(index = 4, type = ProtobufType.UINT32) List validIndexes) implements ProtobufMessage { + @ProtobufProperty(index = 4, type = ProtobufType.UINT32) List validIndexes) { } diff --git a/src/main/java/it/auties/whatsapp/model/signal/auth/NoiseCertificate.java b/src/main/java/it/auties/whatsapp/model/signal/auth/NoiseCertificate.java index f6e0af849..684e4006d 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/auth/NoiseCertificate.java +++ b/src/main/java/it/auties/whatsapp/model/signal/auth/NoiseCertificate.java @@ -1,11 +1,10 @@ package it.auties.whatsapp.model.signal.auth; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("NoiseCertificate") +@ProtobufMessage(name = "NoiseCertificate") public record NoiseCertificate(@ProtobufProperty(index = 1, type = ProtobufType.BYTES) byte[] details, - @ProtobufProperty(index = 2, type = ProtobufType.BYTES) byte[] signature) implements ProtobufMessage { + @ProtobufProperty(index = 2, type = ProtobufType.BYTES) byte[] signature) { } diff --git a/src/main/java/it/auties/whatsapp/model/signal/auth/ServerHello.java b/src/main/java/it/auties/whatsapp/model/signal/auth/ServerHello.java index 93434373c..890354d60 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/auth/ServerHello.java +++ b/src/main/java/it/auties/whatsapp/model/signal/auth/ServerHello.java @@ -1,13 +1,12 @@ package it.auties.whatsapp.model.signal.auth; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import static it.auties.protobuf.model.ProtobufType.BYTES; -@ProtobufMessageName("HandshakeMessage.ServerHello") +@ProtobufMessage(name = "HandshakeMessage.ServerHello") public record ServerHello(@ProtobufProperty(index = 1, type = BYTES) byte[] ephemeral, @ProtobufProperty(index = 2, type = BYTES) byte[] staticText, - @ProtobufProperty(index = 3, type = BYTES) byte[] payload) implements ProtobufMessage { + @ProtobufProperty(index = 3, type = BYTES) byte[] payload) { } diff --git a/src/main/java/it/auties/whatsapp/model/signal/auth/SignedDeviceIdentity.java b/src/main/java/it/auties/whatsapp/model/signal/auth/SignedDeviceIdentity.java index 2219c57e0..3b9555010 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/auth/SignedDeviceIdentity.java +++ b/src/main/java/it/auties/whatsapp/model/signal/auth/SignedDeviceIdentity.java @@ -1,13 +1,12 @@ package it.auties.whatsapp.model.signal.auth; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("ADVSignedDeviceIdentity") +@ProtobufMessage(name = "ADVSignedDeviceIdentity") public record SignedDeviceIdentity(@ProtobufProperty(index = 1, type = ProtobufType.BYTES) byte[] details, @ProtobufProperty(index = 2, type = ProtobufType.BYTES) byte[] accountSignatureKey, @ProtobufProperty(index = 3, type = ProtobufType.BYTES) byte[] accountSignature, - @ProtobufProperty(index = 4, type = ProtobufType.BYTES) byte[] deviceSignature) implements ProtobufMessage { + @ProtobufProperty(index = 4, type = ProtobufType.BYTES) byte[] deviceSignature) { } diff --git a/src/main/java/it/auties/whatsapp/model/signal/auth/SignedDeviceIdentityHMAC.java b/src/main/java/it/auties/whatsapp/model/signal/auth/SignedDeviceIdentityHMAC.java index 84883c25a..c70da5003 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/auth/SignedDeviceIdentityHMAC.java +++ b/src/main/java/it/auties/whatsapp/model/signal/auth/SignedDeviceIdentityHMAC.java @@ -1,11 +1,10 @@ package it.auties.whatsapp.model.signal.auth; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("ADVSignedDeviceIdentityHMAC") +@ProtobufMessage(name = "ADVSignedDeviceIdentityHMAC") public record SignedDeviceIdentityHMAC(@ProtobufProperty(index = 1, type = ProtobufType.BYTES) byte[] details, - @ProtobufProperty(index = 2, type = ProtobufType.BYTES) byte[] hmac) implements ProtobufMessage { + @ProtobufProperty(index = 2, type = ProtobufType.BYTES) byte[] hmac) { } diff --git a/src/main/java/it/auties/whatsapp/model/signal/auth/SignedKeyIndexList.java b/src/main/java/it/auties/whatsapp/model/signal/auth/SignedKeyIndexList.java index a35d6acb5..f943bc5a0 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/auth/SignedKeyIndexList.java +++ b/src/main/java/it/auties/whatsapp/model/signal/auth/SignedKeyIndexList.java @@ -1,11 +1,10 @@ package it.auties.whatsapp.model.signal.auth; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("ADVSignedKeyIndexList") +@ProtobufMessage(name = "ADVSignedKeyIndexList") public record SignedKeyIndexList(@ProtobufProperty(index = 1, type = ProtobufType.BYTES) byte[] details, - @ProtobufProperty(index = 2, type = ProtobufType.BYTES) byte[] accountSignature) implements ProtobufMessage { + @ProtobufProperty(index = 2, type = ProtobufType.BYTES) byte[] accountSignature) { } diff --git a/src/main/java/it/auties/whatsapp/model/signal/auth/UserAgent.java b/src/main/java/it/auties/whatsapp/model/signal/auth/UserAgent.java index a2d378a25..11e2843cc 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/auth/UserAgent.java +++ b/src/main/java/it/auties/whatsapp/model/signal/auth/UserAgent.java @@ -1,15 +1,14 @@ package it.auties.whatsapp.model.signal.auth; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import static it.auties.protobuf.model.ProtobufType.OBJECT; import static it.auties.protobuf.model.ProtobufType.STRING; -@ProtobufMessageName("ClientPayload.UserAgent") +@ProtobufMessage(name = "ClientPayload.UserAgent") public record UserAgent(@ProtobufProperty(index = 1, type = OBJECT) PlatformType platform, @ProtobufProperty(index = 2, type = OBJECT) Version appVersion, @ProtobufProperty(index = 3, type = STRING) String mcc, @@ -22,15 +21,16 @@ public record UserAgent(@ProtobufProperty(index = 1, type = OBJECT) PlatformType @ProtobufProperty(index = 10, type = OBJECT) ReleaseChannel releaseChannel, @ProtobufProperty(index = 11, type = STRING) String localeLanguageIso6391, @ProtobufProperty(index = 12, type = STRING) String localeCountryIso31661Alpha2, - @ProtobufProperty(index = 13, type = STRING) String deviceBoard) implements ProtobufMessage { + @ProtobufProperty(index = 13, type = STRING) String deviceBoard, + @ProtobufProperty(index = 15, type = OBJECT) DeviceType deviceType, + @ProtobufProperty(index = 16, type = STRING) String deviceModelType) { - @ProtobufMessageName("ClientPayload.UserAgent.Platform") - public enum PlatformType implements ProtobufEnum { + @ProtobufEnum(name = "ClientPayload.UserAgent.Platform") + public enum PlatformType { UNKNOWN(999), ANDROID(0), IOS(1), ANDROID_BUSINESS(10), - KAIOS(11), IOS_BUSINESS(12), WINDOWS(13), MACOS(24), @@ -46,10 +46,6 @@ public int index() { return this.index; } - public boolean isWeb() { - return this == WEB; - } - public boolean isAndroid() { return this == ANDROID || this == ANDROID_BUSINESS; } @@ -62,10 +58,6 @@ public boolean isBusiness() { return this == ANDROID_BUSINESS || this == IOS_BUSINESS; } - public boolean isKaiOs() { - return this == KAIOS; - } - public PlatformType toPersonal() { return switch (this) { case ANDROID_BUSINESS -> ANDROID; @@ -83,11 +75,14 @@ public PlatformType toBusiness() { } } - @ProtobufMessageName("ClientPayload.UserAgent.ReleaseChannel") - public enum ReleaseChannel implements ProtobufEnum { + @ProtobufEnum(name = "ClientPayload.UserAgent.ReleaseChannel") + public enum ReleaseChannel { RELEASE(0), + BETA(1), + ALPHA(2), + DEBUG(3); ReleaseChannel(@ProtobufEnumIndex int index) { @@ -100,4 +95,23 @@ public int index() { return this.index; } } + + @ProtobufEnum(name = "ClientPayload.UserAgent.DeviceType") + public enum DeviceType { + PHONE(0), + TABLET(1), + DESKTOP(2), + WEARABLE(3), + VR(4); + + DeviceType(@ProtobufEnumIndex int index) { + this.index = index; + } + + final int index; + + public int index() { + return this.index; + } + } } diff --git a/src/main/java/it/auties/whatsapp/model/signal/auth/Version.java b/src/main/java/it/auties/whatsapp/model/signal/auth/Version.java index 24d0ce6e3..cd861f9ca 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/auth/Version.java +++ b/src/main/java/it/auties/whatsapp/model/signal/auth/Version.java @@ -2,9 +2,8 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.crypto.MD5; import it.auties.whatsapp.util.Validate; @@ -16,7 +15,7 @@ import static java.lang.Integer.parseInt; -@ProtobufMessageName("ClientPayload.UserAgent.AppVersion") +@ProtobufMessage(name = "ClientPayload.UserAgent.AppVersion") public record Version( @ProtobufProperty(index = 1, type = ProtobufType.UINT32) Integer primary, @@ -28,7 +27,7 @@ public record Version( Integer quaternary, @ProtobufProperty(index = 5, type = ProtobufType.UINT32) Integer quinary -) implements ProtobufMessage { +) { public Version(int primary) { this(primary, null, null, null, null); } diff --git a/src/main/java/it/auties/whatsapp/model/signal/auth/WebFeatures.java b/src/main/java/it/auties/whatsapp/model/signal/auth/WebFeatures.java index 2dd8ab851..33ffe0514 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/auth/WebFeatures.java +++ b/src/main/java/it/auties/whatsapp/model/signal/auth/WebFeatures.java @@ -1,14 +1,13 @@ package it.auties.whatsapp.model.signal.auth; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import static it.auties.protobuf.model.ProtobufType.OBJECT; -@ProtobufMessageName("WebFeatures") +@ProtobufMessage(name = "WebFeatures") public record WebFeatures(@ProtobufProperty(index = 1, type = OBJECT) WebFeaturesFlag labelsDisplay, @ProtobufProperty(index = 2, type = OBJECT) WebFeaturesFlag voipIndividualOutgoing, @ProtobufProperty(index = 3, type = OBJECT) WebFeaturesFlag groupsV3, @@ -53,10 +52,10 @@ public record WebFeatures(@ProtobufProperty(index = 1, type = OBJECT) WebFeature @ProtobufProperty(index = 46, type = OBJECT) WebFeaturesFlag mdForceUpgrade, @ProtobufProperty(index = 47, type = OBJECT) WebFeaturesFlag disappearingMode, @ProtobufProperty(index = 48, type = OBJECT) WebFeaturesFlag externalMdOptInAvailable, - @ProtobufProperty(index = 49, type = OBJECT) WebFeaturesFlag noDeleteMessageTimeLimit) implements ProtobufMessage { - - public enum WebFeaturesFlag implements ProtobufEnum { + @ProtobufProperty(index = 49, type = OBJECT) WebFeaturesFlag noDeleteMessageTimeLimit) { + @ProtobufEnum + public enum WebFeaturesFlag { NOT_STARTED(0), FORCE_UPGRADE(1), DEVELOPMENT(2), diff --git a/src/main/java/it/auties/whatsapp/model/signal/auth/WebInfo.java b/src/main/java/it/auties/whatsapp/model/signal/auth/WebInfo.java index 6f9fc3a5b..5dbecb38f 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/auth/WebInfo.java +++ b/src/main/java/it/auties/whatsapp/model/signal/auth/WebInfo.java @@ -1,23 +1,21 @@ package it.auties.whatsapp.model.signal.auth; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import static it.auties.protobuf.model.ProtobufType.OBJECT; import static it.auties.protobuf.model.ProtobufType.STRING; -@ProtobufMessageName("ClientPayload.WebInfo") +@ProtobufMessage(name = "ClientPayload.WebInfo") public record WebInfo(@ProtobufProperty(index = 1, type = STRING) String refToken, @ProtobufProperty(index = 2, type = STRING) String version, @ProtobufProperty(index = 3, type = OBJECT) WebPayload webPayload, - @ProtobufProperty(index = 4, type = OBJECT) Platform webSubPlatform) implements ProtobufMessage { - - @ProtobufMessageName("ClientPayload.UserAgent.Platform") - public enum Platform implements ProtobufEnum { + @ProtobufProperty(index = 4, type = OBJECT) Platform webSubPlatform) { + @ProtobufEnum(name = "ClientPayload.UserAgent.Platform") + public enum Platform { WEB_BROWSER(0), APP_STORE(1), WIN_STORE(2), diff --git a/src/main/java/it/auties/whatsapp/model/signal/auth/WebPayload.java b/src/main/java/it/auties/whatsapp/model/signal/auth/WebPayload.java index b5865351b..4927b2d1d 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/auth/WebPayload.java +++ b/src/main/java/it/auties/whatsapp/model/signal/auth/WebPayload.java @@ -1,13 +1,12 @@ package it.auties.whatsapp.model.signal.auth; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import static it.auties.protobuf.model.ProtobufType.*; -@ProtobufMessageName("ClientPayload.WebInfo.WebdPayload") +@ProtobufMessage(name = "ClientPayload.WebInfo.WebdPayload") public record WebPayload(@ProtobufProperty(index = 1, type = BOOL) boolean usesParticipantInKey, @ProtobufProperty(index = 2, type = BOOL) boolean supportsStarredMessages, @ProtobufProperty(index = 3, type = BOOL) boolean supportsDocumentMessages, @@ -18,5 +17,5 @@ public record WebPayload(@ProtobufProperty(index = 1, type = BOOL) boolean usesP @ProtobufProperty(index = 8, type = BOOL) boolean supportsE2EAudio, @ProtobufProperty(index = 9, type = BOOL) boolean supportsE2EDocument, @ProtobufProperty(index = 10, type = STRING) String documentTypes, - @ProtobufProperty(index = 11, type = BYTES) byte[] features) implements ProtobufMessage { + @ProtobufProperty(index = 11, type = BYTES) byte[] features) { } diff --git a/src/main/java/it/auties/whatsapp/model/signal/keypair/ISignalKeyPair.java b/src/main/java/it/auties/whatsapp/model/signal/keypair/ISignalKeyPair.java index b538d236f..2bbeb76c1 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/keypair/ISignalKeyPair.java +++ b/src/main/java/it/auties/whatsapp/model/signal/keypair/ISignalKeyPair.java @@ -16,7 +16,7 @@ static byte[] toSignalKey(byte[] key) { var result = new byte[33]; System.arraycopy(key, 0, result, 1, key.length); result[0] = 5; - yield result; + yield result; } default -> throw new IllegalArgumentException("Invalid key size: %s".formatted(key.length)); }; diff --git a/src/main/java/it/auties/whatsapp/model/signal/keypair/SignalKeyPair.java b/src/main/java/it/auties/whatsapp/model/signal/keypair/SignalKeyPair.java index c0a51315e..9f5dffd18 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/keypair/SignalKeyPair.java +++ b/src/main/java/it/auties/whatsapp/model/signal/keypair/SignalKeyPair.java @@ -1,19 +1,20 @@ package it.auties.whatsapp.model.signal.keypair; import it.auties.curve25519.Curve25519; +import it.auties.protobuf.annotation.ProtobufMessage; 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.util.Arrays; +@ProtobufMessage public record SignalKeyPair( @ProtobufProperty(index = 1, type = ProtobufType.BYTES) byte[] publicKey, @ProtobufProperty(index = 2, type = ProtobufType.BYTES) byte[] privateKey -) implements ISignalKeyPair, ProtobufMessage { +) implements ISignalKeyPair { public SignalKeyPair(byte[] publicKey, byte[] privateKey) { this.publicKey = ISignalKeyPair.toCurveKey(publicKey); this.privateKey = privateKey; diff --git a/src/main/java/it/auties/whatsapp/model/signal/keypair/SignalPreKeyPair.java b/src/main/java/it/auties/whatsapp/model/signal/keypair/SignalPreKeyPair.java index 884fa8b5f..197221a4c 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/keypair/SignalPreKeyPair.java +++ b/src/main/java/it/auties/whatsapp/model/signal/keypair/SignalPreKeyPair.java @@ -1,10 +1,11 @@ package it.auties.whatsapp.model.signal.keypair; +import it.auties.protobuf.annotation.ProtobufMessage; 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; +@ProtobufMessage public record SignalPreKeyPair( @ProtobufProperty(index = 1, type = ProtobufType.INT32) int id, @@ -12,7 +13,7 @@ public record SignalPreKeyPair( byte[] publicKey, @ProtobufProperty(index = 3, type = ProtobufType.BYTES) byte[] privateKey -) implements ISignalKeyPair, ProtobufMessage { +) implements ISignalKeyPair { public SignalPreKeyPair(int id, byte[] publicKey, byte[] privateKey) { this.id = id; this.publicKey = ISignalKeyPair.toCurveKey(publicKey); diff --git a/src/main/java/it/auties/whatsapp/model/signal/keypair/SignalSignedKeyPair.java b/src/main/java/it/auties/whatsapp/model/signal/keypair/SignalSignedKeyPair.java index ac85163b4..45e4fcedb 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/keypair/SignalSignedKeyPair.java +++ b/src/main/java/it/auties/whatsapp/model/signal/keypair/SignalSignedKeyPair.java @@ -1,8 +1,8 @@ package it.auties.whatsapp.model.signal.keypair; import it.auties.curve25519.Curve25519; +import it.auties.protobuf.annotation.ProtobufMessage; 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 it.auties.whatsapp.util.Bytes; @@ -10,6 +10,7 @@ import java.util.NoSuchElementException; import java.util.Optional; +@ProtobufMessage public record SignalSignedKeyPair( @ProtobufProperty(index = 1, type = ProtobufType.INT32) int id, @@ -17,7 +18,7 @@ public record SignalSignedKeyPair( SignalKeyPair keyPair, @ProtobufProperty(index = 3, type = ProtobufType.BYTES) byte[] signature -) implements ISignalKeyPair, ProtobufMessage { +) implements ISignalKeyPair { public static SignalSignedKeyPair of(int id, SignalKeyPair identityKeyPair) { var keyPair = SignalKeyPair.random(); var signature = Curve25519.sign(identityKeyPair.privateKey(), keyPair.signalPublicKey(), true); diff --git a/src/main/java/it/auties/whatsapp/model/signal/message/SenderKeyMessage.java b/src/main/java/it/auties/whatsapp/model/signal/message/SenderKeyMessage.java index cb9df37b4..8838b46e0 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/message/SenderKeyMessage.java +++ b/src/main/java/it/auties/whatsapp/model/signal/message/SenderKeyMessage.java @@ -2,16 +2,16 @@ import com.fasterxml.jackson.annotation.JsonCreator; import it.auties.curve25519.Curve25519; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.util.Bytes; import java.util.Arrays; -import static it.auties.whatsapp.util.Specification.Signal.SIGNATURE_LENGTH; +import static it.auties.whatsapp.util.SignalConstants.SIGNATURE_LENGTH; -@ProtobufMessageName("SenderKeyMessage") +@ProtobufMessage(name = "SenderKeyMessage") public final class SenderKeyMessage extends SignalProtocolMessage { @ProtobufProperty(index = 1, type = ProtobufType.UINT32) private final Integer id; diff --git a/src/main/java/it/auties/whatsapp/model/signal/message/SignalDistributionMessage.java b/src/main/java/it/auties/whatsapp/model/signal/message/SignalDistributionMessage.java index c85734a00..efd2d3099 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/message/SignalDistributionMessage.java +++ b/src/main/java/it/auties/whatsapp/model/signal/message/SignalDistributionMessage.java @@ -1,14 +1,14 @@ package it.auties.whatsapp.model.signal.message; import com.fasterxml.jackson.annotation.JsonCreator; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.util.Bytes; import java.util.Arrays; -@ProtobufMessageName("SenderKeyDistributionMessage") +@ProtobufMessage(name = "SenderKeyDistributionMessage") public final class SignalDistributionMessage extends SignalProtocolMessage { @ProtobufProperty(index = 1, type = ProtobufType.UINT32) private final Integer id; diff --git a/src/main/java/it/auties/whatsapp/model/signal/message/SignalMessage.java b/src/main/java/it/auties/whatsapp/model/signal/message/SignalMessage.java index ed83da1b2..a47bf530c 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/message/SignalMessage.java +++ b/src/main/java/it/auties/whatsapp/model/signal/message/SignalMessage.java @@ -1,7 +1,7 @@ package it.auties.whatsapp.model.signal.message; import com.fasterxml.jackson.annotation.JsonCreator; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.util.Bytes; @@ -9,9 +9,9 @@ import java.util.Arrays; import java.util.Objects; -import static it.auties.whatsapp.util.Specification.Signal.MAC_LENGTH; +import static it.auties.whatsapp.util.SignalConstants.MAC_LENGTH; -@ProtobufMessageName("SignalMessage") +@ProtobufMessage(name = "SignalMessage") public final class SignalMessage extends SignalProtocolMessage { @ProtobufProperty(index = 1, type = ProtobufType.BYTES) private final byte[] ephemeralPublicKey; diff --git a/src/main/java/it/auties/whatsapp/model/signal/message/SignalPreKeyMessage.java b/src/main/java/it/auties/whatsapp/model/signal/message/SignalPreKeyMessage.java index dfc079fa5..b4751dbc5 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/message/SignalPreKeyMessage.java +++ b/src/main/java/it/auties/whatsapp/model/signal/message/SignalPreKeyMessage.java @@ -1,14 +1,14 @@ package it.auties.whatsapp.model.signal.message; import com.fasterxml.jackson.annotation.JsonCreator; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.util.Bytes; import java.util.Arrays; -@ProtobufMessageName("PreKeySignalMessage") +@ProtobufMessage(name = "PreKeySignalMessage") public final class SignalPreKeyMessage extends SignalProtocolMessage { @ProtobufProperty(index = 1, type = ProtobufType.UINT32) private final Integer preKeyId; diff --git a/src/main/java/it/auties/whatsapp/model/signal/message/SignalProtocolMessage.java b/src/main/java/it/auties/whatsapp/model/signal/message/SignalProtocolMessage.java index 3537ab060..a9c388976 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/message/SignalProtocolMessage.java +++ b/src/main/java/it/auties/whatsapp/model/signal/message/SignalProtocolMessage.java @@ -1,15 +1,14 @@ package it.auties.whatsapp.model.signal.message; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.whatsapp.util.Bytes; -import it.auties.whatsapp.util.Specification; +import it.auties.whatsapp.util.SignalConstants; -public abstract sealed class SignalProtocolMessage> implements ProtobufMessage permits SenderKeyMessage, SignalDistributionMessage, SignalMessage, SignalPreKeyMessage { - private int version; +public abstract sealed class SignalProtocolMessage> permits SenderKeyMessage, SignalDistributionMessage, SignalMessage, SignalPreKeyMessage { protected byte[] serialized; + private int version; public SignalProtocolMessage() { - this.version = Specification.Signal.CURRENT_VERSION; + this.version = SignalConstants.CURRENT_VERSION; } public SignalProtocolMessage(int version, byte[] serialized) { diff --git a/src/main/java/it/auties/whatsapp/model/signal/sender/SenderChainKey.java b/src/main/java/it/auties/whatsapp/model/signal/sender/SenderChainKey.java index af36166b1..45587ad18 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/sender/SenderChainKey.java +++ b/src/main/java/it/auties/whatsapp/model/signal/sender/SenderChainKey.java @@ -1,16 +1,17 @@ package it.auties.whatsapp.model.signal.sender; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.crypto.Hmac; +@ProtobufMessage public record SenderChainKey( @ProtobufProperty(index = 1, type = ProtobufType.INT32) int iteration, @ProtobufProperty(index = 2, type = ProtobufType.BYTES) byte[] seed -) implements ProtobufMessage { +) { private static final byte[] MESSAGE_KEY_SEED = {0x01}; private static final byte[] CHAIN_KEY_SEED = {0x02}; diff --git a/src/main/java/it/auties/whatsapp/model/signal/sender/SenderKeyName.java b/src/main/java/it/auties/whatsapp/model/signal/sender/SenderKeyName.java index 63bbd3100..52420db89 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/sender/SenderKeyName.java +++ b/src/main/java/it/auties/whatsapp/model/signal/sender/SenderKeyName.java @@ -2,12 +2,13 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; -import it.auties.protobuf.annotation.ProtobufConverter; +import it.auties.protobuf.annotation.ProtobufDeserializer; +import it.auties.protobuf.annotation.ProtobufSerializer; import it.auties.whatsapp.model.signal.session.SessionAddress; public record SenderKeyName(String groupId, SessionAddress sender) { @JsonCreator - @ProtobufConverter + @ProtobufDeserializer public static SenderKeyName of(String serialized) { var split = serialized.split("::", 3); var address = new SessionAddress(split[1], Integer.parseUnsignedInt(split[2])); @@ -15,7 +16,7 @@ public static SenderKeyName of(String serialized) { } @JsonValue - @ProtobufConverter + @ProtobufSerializer @Override public String toString() { return "%s::%s::%s".formatted(groupId(), sender().name(), sender().id()); diff --git a/src/main/java/it/auties/whatsapp/model/signal/sender/SenderKeyRecord.java b/src/main/java/it/auties/whatsapp/model/signal/sender/SenderKeyRecord.java index 9f417b2be..82e535d34 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/sender/SenderKeyRecord.java +++ b/src/main/java/it/auties/whatsapp/model/signal/sender/SenderKeyRecord.java @@ -1,14 +1,15 @@ package it.auties.whatsapp.model.signal.sender; import com.fasterxml.jackson.annotation.JsonCreator; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.signal.keypair.SignalKeyPair; import java.util.*; -public final class SenderKeyRecord implements ProtobufMessage { +@ProtobufMessage +public final class SenderKeyRecord { @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) private final List states; diff --git a/src/main/java/it/auties/whatsapp/model/signal/sender/SenderKeyState.java b/src/main/java/it/auties/whatsapp/model/signal/sender/SenderKeyState.java index 27fbaffac..aacc2254d 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/sender/SenderKeyState.java +++ b/src/main/java/it/auties/whatsapp/model/signal/sender/SenderKeyState.java @@ -1,8 +1,8 @@ package it.auties.whatsapp.model.signal.sender; import com.fasterxml.jackson.annotation.JsonCreator; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.signal.keypair.SignalKeyPair; @@ -12,12 +12,13 @@ import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; -public final class SenderKeyState implements ProtobufMessage { +@ProtobufMessage +public final class SenderKeyState { @ProtobufProperty(index = 1, type = ProtobufType.INT32) private final int id; @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) private final SignalKeyPair signingKey; - @ProtobufProperty(index = 3, type = ProtobufType.MAP, keyType = ProtobufType.INT32, valueType = ProtobufType.OBJECT) + @ProtobufProperty(index = 3, type = ProtobufType.MAP, mapKeyType = ProtobufType.INT32, mapValueType = ProtobufType.OBJECT) private final ConcurrentHashMap messageKeys; @ProtobufProperty(index = 4, type = ProtobufType.OBJECT) private SenderChainKey chainKey; diff --git a/src/main/java/it/auties/whatsapp/model/signal/sender/SenderMessageKey.java b/src/main/java/it/auties/whatsapp/model/signal/sender/SenderMessageKey.java index 2a1ca7a3b..4eb20155f 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/sender/SenderMessageKey.java +++ b/src/main/java/it/auties/whatsapp/model/signal/sender/SenderMessageKey.java @@ -1,15 +1,16 @@ package it.auties.whatsapp.model.signal.sender; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.crypto.Hkdf; import it.auties.whatsapp.util.Bytes; -import it.auties.whatsapp.util.Specification; +import it.auties.whatsapp.util.SignalConstants; import java.nio.charset.StandardCharsets; import java.util.Arrays; +@ProtobufMessage public record SenderMessageKey( @ProtobufProperty(index = 1, type = ProtobufType.INT32) int iteration, @@ -19,21 +20,21 @@ public record SenderMessageKey( byte[] iv, @ProtobufProperty(index = 4, type = ProtobufType.BYTES) byte[] cipherKey -) implements ProtobufMessage { +) { public SenderMessageKey(int iteration, byte[] seed) { this(iteration, seed, createIv(seed), createCipherKey(seed)); } private static byte[] createIv(byte[] seed) { var derivative = getDerivedSeed(seed); - return Arrays.copyOf(derivative[0], Specification.Signal.IV_LENGTH); + return Arrays.copyOf(derivative[0], SignalConstants.IV_LENGTH); } private static byte[] createCipherKey(byte[] seed) { var derivative = getDerivedSeed(seed); - var data = Arrays.copyOfRange(derivative[0], Specification.Signal.IV_LENGTH, derivative[0].length); + var data = Arrays.copyOfRange(derivative[0], SignalConstants.IV_LENGTH, derivative[0].length); var concat = Bytes.concat(data, derivative[1]); - return Arrays.copyOf(concat, Specification.Signal.KEY_LENGTH); + return Arrays.copyOf(concat, SignalConstants.KEY_LENGTH); } private static byte[][] getDerivedSeed(byte[] seed) { diff --git a/src/main/java/it/auties/whatsapp/model/signal/sender/SenderPreKeys.java b/src/main/java/it/auties/whatsapp/model/signal/sender/SenderPreKeys.java index a8808a742..36b5c326e 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/sender/SenderPreKeys.java +++ b/src/main/java/it/auties/whatsapp/model/signal/sender/SenderPreKeys.java @@ -1,7 +1,7 @@ package it.auties.whatsapp.model.signal.sender; +import it.auties.protobuf.annotation.ProtobufMessage; 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; @@ -10,7 +10,8 @@ import java.util.Collections; import java.util.Objects; -public final class SenderPreKeys implements ProtobufMessage { +@ProtobufMessage +public final class SenderPreKeys { @ProtobufProperty(index = 1, type = ProtobufType.STRING) private final Collection preKeys; diff --git a/src/main/java/it/auties/whatsapp/model/signal/session/Session.java b/src/main/java/it/auties/whatsapp/model/signal/session/Session.java index 279cfd89e..dc0bffdac 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/session/Session.java +++ b/src/main/java/it/auties/whatsapp/model/signal/session/Session.java @@ -1,8 +1,8 @@ package it.auties.whatsapp.model.signal.session; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.Collection; @@ -11,7 +11,8 @@ import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; -public final class Session implements ProtobufMessage { +@ProtobufMessage +public final class Session { @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) private final ConcurrentHashMap.KeySetView states; diff --git a/src/main/java/it/auties/whatsapp/model/signal/session/SessionAddress.java b/src/main/java/it/auties/whatsapp/model/signal/session/SessionAddress.java index 07d06275e..6ad6665f0 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/session/SessionAddress.java +++ b/src/main/java/it/auties/whatsapp/model/signal/session/SessionAddress.java @@ -2,14 +2,15 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonValue; -import it.auties.protobuf.annotation.ProtobufConverter; +import it.auties.protobuf.annotation.ProtobufDeserializer; +import it.auties.protobuf.annotation.ProtobufSerializer; import it.auties.whatsapp.util.Validate; import java.util.Objects; public record SessionAddress(String name, int id) { @JsonCreator - @ProtobufConverter + @ProtobufDeserializer public static SessionAddress of(String serialized) { var split = serialized.split(":", 2); Validate.isTrue(split.length == 2, "Too few parts"); @@ -17,7 +18,7 @@ public static SessionAddress of(String serialized) { } @JsonValue - @ProtobufConverter + @ProtobufSerializer @Override public String toString() { return "%s:%s".formatted(name(), id()); diff --git a/src/main/java/it/auties/whatsapp/model/signal/session/SessionChain.java b/src/main/java/it/auties/whatsapp/model/signal/session/SessionChain.java index d9257f06f..25dc0020a 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/session/SessionChain.java +++ b/src/main/java/it/auties/whatsapp/model/signal/session/SessionChain.java @@ -1,22 +1,23 @@ package it.auties.whatsapp.model.signal.session; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +@ProtobufMessage public record SessionChain( @ProtobufProperty(index = 1, type = ProtobufType.INT32) AtomicInteger counter, @ProtobufProperty(index = 2, type = ProtobufType.BYTES) AtomicReference key, - @ProtobufProperty(index = 3, type = ProtobufType.MAP, keyType = ProtobufType.INT32, valueType = ProtobufType.BYTES) + @ProtobufProperty(index = 3, type = ProtobufType.MAP, mapKeyType = ProtobufType.INT32, mapValueType = ProtobufType.BYTES) ConcurrentHashMap messageKeys -) implements ProtobufMessage { +) { public SessionChain(int counter, byte[] key) { this(new AtomicInteger(counter), new AtomicReference<>(key), new ConcurrentHashMap<>()); } diff --git a/src/main/java/it/auties/whatsapp/model/signal/session/SessionPreKey.java b/src/main/java/it/auties/whatsapp/model/signal/session/SessionPreKey.java index cb2dfa288..3c5d24bc5 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/session/SessionPreKey.java +++ b/src/main/java/it/auties/whatsapp/model/signal/session/SessionPreKey.java @@ -1,10 +1,11 @@ package it.auties.whatsapp.model.signal.session; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; +@ProtobufMessage public record SessionPreKey( @ProtobufProperty(index = 1, type = ProtobufType.INT32) Integer preKeyId, @@ -12,6 +13,6 @@ public record SessionPreKey( byte[] baseKey, @ProtobufProperty(index = 3, type = ProtobufType.INT32) int signedKeyId -) implements ProtobufMessage { +) { } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/signal/session/SessionState.java b/src/main/java/it/auties/whatsapp/model/signal/session/SessionState.java index 6687e0365..7c4bb2582 100644 --- a/src/main/java/it/auties/whatsapp/model/signal/session/SessionState.java +++ b/src/main/java/it/auties/whatsapp/model/signal/session/SessionState.java @@ -1,20 +1,20 @@ package it.auties.whatsapp.model.signal.session; import com.fasterxml.jackson.annotation.JsonCreator; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.signal.keypair.SignalKeyPair; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -public final class SessionState implements ProtobufMessage { +@ProtobufMessage +public final class SessionState { @ProtobufProperty(index = 1, type = ProtobufType.INT32) private final int version; @ProtobufProperty(index = 2, type = ProtobufType.INT32) - private final int registrationId; @ProtobufProperty(index = 3, type = ProtobufType.BYTES) @@ -23,7 +23,7 @@ public final class SessionState implements ProtobufMessage { @ProtobufProperty(index = 4, type = ProtobufType.BYTES) private final byte[] remoteIdentityKey; - @ProtobufProperty(index = 5, type = ProtobufType.MAP, keyType = ProtobufType.STRING, valueType = ProtobufType.OBJECT) + @ProtobufProperty(index = 5, type = ProtobufType.MAP, mapKeyType = ProtobufType.STRING, mapValueType = ProtobufType.OBJECT) private final ConcurrentHashMap chains; @ProtobufProperty(index = 6, type = ProtobufType.BYTES) diff --git a/src/main/java/it/auties/whatsapp/model/sync/ActionDataSync.java b/src/main/java/it/auties/whatsapp/model/sync/ActionDataSync.java index 2ea7c839f..4719e6ea4 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/ActionDataSync.java +++ b/src/main/java/it/auties/whatsapp/model/sync/ActionDataSync.java @@ -1,14 +1,13 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.info.MessageIndexInfo; import java.nio.charset.StandardCharsets; -@ProtobufMessageName("SyncActionData") +@ProtobufMessage(name = "SyncActionData") public record ActionDataSync( @ProtobufProperty(index = 1, type = ProtobufType.BYTES) byte[] index, @@ -18,7 +17,7 @@ public record ActionDataSync( byte[] padding, @ProtobufProperty(index = 4, type = ProtobufType.INT32) Integer version -) implements ProtobufMessage { +) { public MessageIndexInfo messageIndex() { var jsonIndex = new String(index, StandardCharsets.UTF_8); return MessageIndexInfo.ofJson(jsonIndex); diff --git a/src/main/java/it/auties/whatsapp/model/sync/ActionMessageRangeSync.java b/src/main/java/it/auties/whatsapp/model/sync/ActionMessageRangeSync.java index 281061376..b6c4e4d04 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/ActionMessageRangeSync.java +++ b/src/main/java/it/auties/whatsapp/model/sync/ActionMessageRangeSync.java @@ -1,9 +1,8 @@ package it.auties.whatsapp.model.sync; import com.fasterxml.jackson.annotation.JsonCreator; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.chat.Chat; import it.auties.whatsapp.model.info.ChatMessageInfo; @@ -12,14 +11,12 @@ import java.util.Collections; import java.util.List; -@ProtobufMessageName("SyncActionValue.SyncActionMessageRange") -public final class ActionMessageRangeSync implements ProtobufMessage { +@ProtobufMessage(name = "SyncActionValue.SyncActionMessageRange") +public final class ActionMessageRangeSync { @ProtobufProperty(index = 1, type = ProtobufType.INT64) private Long lastMessageTimestamp; - @ProtobufProperty(index = 2, type = ProtobufType.INT64) private Long lastSystemMessageTimestamp; - @ProtobufProperty(index = 3, type = ProtobufType.OBJECT) private final List messages; diff --git a/src/main/java/it/auties/whatsapp/model/sync/ActionValueSync.java b/src/main/java/it/auties/whatsapp/model/sync/ActionValueSync.java index 4bd21758c..aad0f5af2 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/ActionValueSync.java +++ b/src/main/java/it/auties/whatsapp/model/sync/ActionValueSync.java @@ -1,8 +1,7 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.action.*; import it.auties.whatsapp.model.setting.*; @@ -10,7 +9,7 @@ import java.util.Optional; -@ProtobufMessageName("SyncActionValue") +@ProtobufMessage(name = "SyncActionValue") public record ActionValueSync( @ProtobufProperty(index = 1, type = ProtobufType.INT64) long timestamp, @@ -78,7 +77,7 @@ public record ActionValueSync( Optional keyExpiration, @ProtobufProperty(index = 24, type = ProtobufType.OBJECT) Optional primaryFeature -) implements ProtobufMessage { +) { public static ActionValueSync of(Action action) { var builder = new ActionValueSyncBuilder().timestamp(Clock.nowSeconds()); switch (action) { diff --git a/src/main/java/it/auties/whatsapp/model/sync/AppStateFatalExceptionNotification.java b/src/main/java/it/auties/whatsapp/model/sync/AppStateFatalExceptionNotification.java index cf244fcb4..50f67e2c8 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/AppStateFatalExceptionNotification.java +++ b/src/main/java/it/auties/whatsapp/model/sync/AppStateFatalExceptionNotification.java @@ -1,16 +1,15 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import java.util.List; import static it.auties.protobuf.model.ProtobufType.INT64; import static it.auties.protobuf.model.ProtobufType.STRING; -@ProtobufMessageName("Message.AppStateFatalExceptionNotification") +@ProtobufMessage(name = "Message.AppStateFatalExceptionNotification") public record AppStateFatalExceptionNotification( @ProtobufProperty(index = 1, type = STRING) List collectionNames, - @ProtobufProperty(index = 2, type = INT64) Long timestamp) implements ProtobufMessage { + @ProtobufProperty(index = 2, type = INT64) Long timestamp) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKey.java b/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKey.java index 23ecd349d..3853966f9 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKey.java +++ b/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKey.java @@ -1,15 +1,14 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import static it.auties.protobuf.model.ProtobufType.OBJECT; -@ProtobufMessageName("Message.AppStateSyncKey") +@ProtobufMessage(name = "Message.AppStateSyncKey") public record AppStateSyncKey( @ProtobufProperty(index = 1, type = OBJECT) AppStateSyncKeyId keyId, @ProtobufProperty(index = 2, type = OBJECT) AppStateSyncKeyData keyData -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKeyData.java b/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKeyData.java index b6f74360c..0860186ef 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKeyData.java +++ b/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKeyData.java @@ -1,13 +1,12 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import static it.auties.protobuf.model.ProtobufType.*; -@ProtobufMessageName("Message.AppStateSyncKeyData") +@ProtobufMessage(name = "Message.AppStateSyncKeyData") public record AppStateSyncKeyData(@ProtobufProperty(index = 1, type = BYTES) byte[] keyData, @ProtobufProperty(index = 2, type = OBJECT) AppStateSyncKeyFingerprint fingerprint, - @ProtobufProperty(index = 3, type = INT64) Long timestamp) implements ProtobufMessage { + @ProtobufProperty(index = 3, type = INT64) Long timestamp) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKeyFingerprint.java b/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKeyFingerprint.java index 1a32a3235..0700cec61 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKeyFingerprint.java +++ b/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKeyFingerprint.java @@ -1,15 +1,14 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import java.util.List; import static it.auties.protobuf.model.ProtobufType.UINT32; -@ProtobufMessageName("Message.AppStateSyncKeyFingerprint") +@ProtobufMessage(name = "Message.AppStateSyncKeyFingerprint") public record AppStateSyncKeyFingerprint(@ProtobufProperty(index = 1, type = UINT32) Integer rawId, @ProtobufProperty(index = 2, type = UINT32) Integer currentIndex, - @ProtobufProperty(index = 3, type = UINT32, packed = true) List deviceIndexes) implements ProtobufMessage { + @ProtobufProperty(index = 3, type = UINT32, packed = true) List deviceIndexes) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKeyId.java b/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKeyId.java index 90f02bc85..93879e0ca 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKeyId.java +++ b/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKeyId.java @@ -1,14 +1,13 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import static it.auties.protobuf.model.ProtobufType.BYTES; -@ProtobufMessageName("Message.AppStateSyncKeyId") +@ProtobufMessage(name = "Message.AppStateSyncKeyId") public record AppStateSyncKeyId( @ProtobufProperty(index = 1, type = BYTES) byte[] keyId -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKeyRequest.java b/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKeyRequest.java index 74c509ecc..106f50e94 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKeyRequest.java +++ b/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKeyRequest.java @@ -1,14 +1,13 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import java.util.List; import static it.auties.protobuf.model.ProtobufType.OBJECT; -@ProtobufMessageName("Message.AppStateSyncKeyRequest") +@ProtobufMessage(name = "Message.AppStateSyncKeyRequest") public record AppStateSyncKeyRequest( - @ProtobufProperty(index = 1, type = OBJECT) List keyIds) implements ProtobufMessage { + @ProtobufProperty(index = 1, type = OBJECT) List keyIds) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKeyShare.java b/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKeyShare.java index 5d54aa9cd..fade052ae 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKeyShare.java +++ b/src/main/java/it/auties/whatsapp/model/sync/AppStateSyncKeyShare.java @@ -1,14 +1,13 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import java.util.List; import static it.auties.protobuf.model.ProtobufType.OBJECT; -@ProtobufMessageName("Message.AppStateSyncKeyShare") +@ProtobufMessage(name = "Message.AppStateSyncKeyShare") public record AppStateSyncKeyShare( - @ProtobufProperty(index = 1, type = OBJECT) List keys) implements ProtobufMessage { + @ProtobufProperty(index = 1, type = OBJECT) List keys) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/DeviceListMetadata.java b/src/main/java/it/auties/whatsapp/model/sync/DeviceListMetadata.java index 570786d47..40408ef68 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/DeviceListMetadata.java +++ b/src/main/java/it/auties/whatsapp/model/sync/DeviceListMetadata.java @@ -1,18 +1,17 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import java.util.List; import static it.auties.protobuf.model.ProtobufType.*; -@ProtobufMessageName("DeviceListMetadata") +@ProtobufMessage(name = "DeviceListMetadata") public record DeviceListMetadata(@ProtobufProperty(index = 1, type = BYTES) byte[] senderKeyHash, @ProtobufProperty(index = 2, type = UINT64) Long senderTimestamp, @ProtobufProperty(index = 3, type = UINT32, packed = true) List senderKeyIndexes, @ProtobufProperty(index = 8, type = BYTES) byte[] recipientKeyHash, @ProtobufProperty(index = 9, type = UINT64) Long recipientTimestamp, - @ProtobufProperty(index = 10, type = UINT32, packed = true) List recipientKeyIndexes) implements ProtobufMessage { + @ProtobufProperty(index = 10, type = UINT32, packed = true) List recipientKeyIndexes) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/ExitCode.java b/src/main/java/it/auties/whatsapp/model/sync/ExitCode.java index 4fb0d5265..4c068d78e 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/ExitCode.java +++ b/src/main/java/it/auties/whatsapp/model/sync/ExitCode.java @@ -1,13 +1,12 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import static it.auties.protobuf.model.ProtobufType.STRING; import static it.auties.protobuf.model.ProtobufType.UINT64; -@ProtobufMessageName("ExitCode") +@ProtobufMessage(name = "ExitCode") public record ExitCode(@ProtobufProperty(index = 1, type = UINT64) long code, - @ProtobufProperty(index = 2, type = STRING) String text) implements ProtobufMessage { + @ProtobufProperty(index = 2, type = STRING) String text) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/ExternalBlobReference.java b/src/main/java/it/auties/whatsapp/model/sync/ExternalBlobReference.java index e53b3bc63..962fd3689 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/ExternalBlobReference.java +++ b/src/main/java/it/auties/whatsapp/model/sync/ExternalBlobReference.java @@ -1,7 +1,7 @@ package it.auties.whatsapp.model.sync; import com.fasterxml.jackson.annotation.JsonCreator; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; import it.auties.whatsapp.model.media.AttachmentType; import it.auties.whatsapp.model.media.MutableAttachmentProvider; @@ -11,7 +11,7 @@ import static it.auties.protobuf.model.ProtobufType.*; -@ProtobufMessageName("ExternalBlobReference") +@ProtobufMessage(name = "ExternalBlobReference") public final class ExternalBlobReference implements MutableAttachmentProvider { @ProtobufProperty(index = 1, type = BYTES) private byte[] mediaKey; diff --git a/src/main/java/it/auties/whatsapp/model/sync/HistorySync.java b/src/main/java/it/auties/whatsapp/model/sync/HistorySync.java index c24ef7203..3034b735a 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/HistorySync.java +++ b/src/main/java/it/auties/whatsapp/model/sync/HistorySync.java @@ -1,10 +1,9 @@ package it.auties.whatsapp.model.sync; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.whatsapp.model.chat.Chat; import it.auties.whatsapp.model.chat.GroupPastParticipants; import it.auties.whatsapp.model.info.ChatMessageInfo; @@ -15,7 +14,7 @@ import static it.auties.protobuf.model.ProtobufType.*; -@ProtobufMessageName("HistorySync") +@ProtobufMessage(name = "HistorySync") public record HistorySync(@ProtobufProperty(index = 1, type = OBJECT, required = true) Type syncType, @ProtobufProperty(index = 2, type = OBJECT) List conversations, @ProtobufProperty(index = 3, type = OBJECT) List statusV3Messages, @@ -28,14 +27,13 @@ public record HistorySync(@ProtobufProperty(index = 1, type = OBJECT, required = @ProtobufProperty(index = 11, type = OBJECT) List recentStickers, @ProtobufProperty(index = 12, type = OBJECT) - List pastParticipants) implements ProtobufMessage { + List pastParticipants) { public HistorySync { Objects.requireNonNull(syncType, "Missing mandatory field: syncType"); } - @ProtobufMessageName("HistorySync.HistorySyncType") - public enum Type implements ProtobufEnum { - + @ProtobufEnum(name = "HistorySync.HistorySyncType") + public enum Type { INITIAL_BOOTSTRAP(0), INITIAL_STATUS_V3(1), FULL(2), diff --git a/src/main/java/it/auties/whatsapp/model/sync/HistorySyncConfig.java b/src/main/java/it/auties/whatsapp/model/sync/HistorySyncConfig.java index 3c54a8edd..be0942727 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/HistorySyncConfig.java +++ b/src/main/java/it/auties/whatsapp/model/sync/HistorySyncConfig.java @@ -1,11 +1,10 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("DeviceProps.HistorySyncConfig") +@ProtobufMessage(name = "DeviceProps.HistorySyncConfig") public record HistorySyncConfig( @ProtobufProperty(index = 1, type = ProtobufType.UINT32) Integer fullSyncDaysLimit, @@ -21,6 +20,6 @@ public record HistorySyncConfig( Boolean supportCallLogHistory, @ProtobufProperty(index = 7, type = ProtobufType.BOOL) Boolean supportBotUserAgentChatHistory -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/HistorySyncMessage.java b/src/main/java/it/auties/whatsapp/model/sync/HistorySyncMessage.java index 8b2bc7ff0..18420ecaa 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/HistorySyncMessage.java +++ b/src/main/java/it/auties/whatsapp/model/sync/HistorySyncMessage.java @@ -1,20 +1,19 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import it.auties.whatsapp.model.info.ChatMessageInfo; import java.util.Objects; -@ProtobufMessageName("HistorySyncMsg") +@ProtobufMessage(name = "HistorySyncMsg") public record HistorySyncMessage( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) ChatMessageInfo messageInfo, @ProtobufProperty(index = 2, type = ProtobufType.UINT64) long messageOrderId -) implements ProtobufMessage { +) { @Override public boolean equals(Object obj) { return obj instanceof HistorySyncMessage that && Objects.equals(messageInfo, that.messageInfo()); diff --git a/src/main/java/it/auties/whatsapp/model/sync/HistorySyncNotification.java b/src/main/java/it/auties/whatsapp/model/sync/HistorySyncNotification.java index d5fbf360f..7162d0177 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/HistorySyncNotification.java +++ b/src/main/java/it/auties/whatsapp/model/sync/HistorySyncNotification.java @@ -1,10 +1,10 @@ package it.auties.whatsapp.model.sync; import com.fasterxml.jackson.annotation.JsonCreator; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; import it.auties.whatsapp.model.media.AttachmentType; import it.auties.whatsapp.model.media.MutableAttachmentProvider; @@ -13,7 +13,7 @@ import static it.auties.protobuf.model.ProtobufType.*; -@ProtobufMessageName("Message.HistorySyncNotification") +@ProtobufMessage(name = "Message.HistorySyncNotification") public final class HistorySyncNotification implements MutableAttachmentProvider { @ProtobufProperty(index = 1, type = BYTES) private byte[] mediaSha256; @@ -160,9 +160,8 @@ public Optional peerDataRequestSessionId() { return Optional.ofNullable(peerDataRequestSessionId); } - @ProtobufMessageName("Message.HistorySyncNotification.HistorySyncType") - public enum Type implements ProtobufEnum { - + @ProtobufEnum(name = "Message.HistorySyncNotification.HistorySyncType") + public enum Type { INITIAL_BOOTSTRAP(0), INITIAL_STATUS_V3(1), FULL(2), diff --git a/src/main/java/it/auties/whatsapp/model/sync/IndexSync.java b/src/main/java/it/auties/whatsapp/model/sync/IndexSync.java index cd6bd578d..7bbb7dea7 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/IndexSync.java +++ b/src/main/java/it/auties/whatsapp/model/sync/IndexSync.java @@ -1,14 +1,13 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("SyncdIndex") +@ProtobufMessage(name = "SyncdIndex") public record IndexSync( @ProtobufProperty(index = 1, type = ProtobufType.BYTES) byte[] blob -) implements ProtobufMessage { +) { } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/sync/InitialSecurityNotificationSettingSync.java b/src/main/java/it/auties/whatsapp/model/sync/InitialSecurityNotificationSettingSync.java index 7dfdb9868..9602f8fcf 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/InitialSecurityNotificationSettingSync.java +++ b/src/main/java/it/auties/whatsapp/model/sync/InitialSecurityNotificationSettingSync.java @@ -1,12 +1,11 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import static it.auties.protobuf.model.ProtobufType.BOOL; -@ProtobufMessageName("Message.InitialSecurityNotificationSettingSync") +@ProtobufMessage(name = "Message.InitialSecurityNotificationSettingSync") public record InitialSecurityNotificationSettingSync( - @ProtobufProperty(index = 1, type = BOOL) boolean securityNotificationEnabled) implements ProtobufMessage { + @ProtobufProperty(index = 1, type = BOOL) boolean securityNotificationEnabled) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/KeyExpiration.java b/src/main/java/it/auties/whatsapp/model/sync/KeyExpiration.java index fd214b3db..2e2e3aca0 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/KeyExpiration.java +++ b/src/main/java/it/auties/whatsapp/model/sync/KeyExpiration.java @@ -1,12 +1,11 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import static it.auties.protobuf.model.ProtobufType.INT32; -@ProtobufMessageName("SyncActionValue.KeyExpiration") +@ProtobufMessage(name = "SyncActionValue.KeyExpiration") public record KeyExpiration( - @ProtobufProperty(index = 1, type = INT32) Integer expiredKeyEpoch) implements ProtobufMessage { + @ProtobufProperty(index = 1, type = INT32) Integer expiredKeyEpoch) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/KeyId.java b/src/main/java/it/auties/whatsapp/model/sync/KeyId.java index 9412897f7..72055e0e0 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/KeyId.java +++ b/src/main/java/it/auties/whatsapp/model/sync/KeyId.java @@ -1,11 +1,10 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import static it.auties.protobuf.model.ProtobufType.BYTES; -@ProtobufMessageName("KeyId") -public record KeyId(@ProtobufProperty(index = 1, type = BYTES) byte[] id) implements ProtobufMessage { +@ProtobufMessage(name = "KeyId") +public record KeyId(@ProtobufProperty(index = 1, type = BYTES) byte[] id) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/MediaRetryNotification.java b/src/main/java/it/auties/whatsapp/model/sync/MediaRetryNotification.java index 3a7efe2fd..862dd09f0 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/MediaRetryNotification.java +++ b/src/main/java/it/auties/whatsapp/model/sync/MediaRetryNotification.java @@ -1,23 +1,22 @@ package it.auties.whatsapp.model.sync; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import java.util.Optional; import static it.auties.protobuf.model.ProtobufType.OBJECT; import static it.auties.protobuf.model.ProtobufType.STRING; -@ProtobufMessageName("MediaRetryNotification") +@ProtobufMessage(name = "MediaRetryNotification") public record MediaRetryNotification(@ProtobufProperty(index = 1, type = STRING) String stanzaId, @ProtobufProperty(index = 2, type = STRING) Optional directPath, - @ProtobufProperty(index = 3, type = OBJECT) MediaRetryNotificationResultType result) implements ProtobufMessage { - - public enum MediaRetryNotificationResultType implements ProtobufEnum { + @ProtobufProperty(index = 3, type = OBJECT) MediaRetryNotificationResultType result) { + @ProtobufEnum + public enum MediaRetryNotificationResultType { GENERAL_ERROR(0), SUCCESS(1), NOT_FOUND(2), diff --git a/src/main/java/it/auties/whatsapp/model/sync/MutationKeys.java b/src/main/java/it/auties/whatsapp/model/sync/MutationKeys.java index ebce88c34..8c2697609 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/MutationKeys.java +++ b/src/main/java/it/auties/whatsapp/model/sync/MutationKeys.java @@ -1,7 +1,7 @@ package it.auties.whatsapp.model.sync; import it.auties.whatsapp.crypto.Hkdf; -import it.auties.whatsapp.util.Specification; +import it.auties.whatsapp.util.SignalConstants; import java.nio.charset.StandardCharsets; import java.util.Arrays; @@ -12,11 +12,11 @@ public record MutationKeys(byte[] indexKey, byte[] encKey, byte[] macKey, byte[] public static MutationKeys of(byte[] key) { var expanded = Hkdf.extractAndExpand(key, MUTATION_KEYS, EXPANDED_SIZE); - var indexKey = Arrays.copyOfRange(expanded, 0, Specification.Signal.KEY_LENGTH); - var encKey = Arrays.copyOfRange(expanded, Specification.Signal.KEY_LENGTH, Specification.Signal.KEY_LENGTH * 2); - var macKey = Arrays.copyOfRange(expanded, Specification.Signal.KEY_LENGTH * 2, Specification.Signal.KEY_LENGTH * 3); - var snapshotMacKey = Arrays.copyOfRange(expanded, Specification.Signal.KEY_LENGTH * 3, Specification.Signal.KEY_LENGTH * 4); - var patchMacKey = Arrays.copyOfRange(expanded, Specification.Signal.KEY_LENGTH * 4, expanded.length); + var indexKey = Arrays.copyOfRange(expanded, 0, SignalConstants.KEY_LENGTH); + var encKey = Arrays.copyOfRange(expanded, SignalConstants.KEY_LENGTH, SignalConstants.KEY_LENGTH * 2); + var macKey = Arrays.copyOfRange(expanded, SignalConstants.KEY_LENGTH * 2, SignalConstants.KEY_LENGTH * 3); + var snapshotMacKey = Arrays.copyOfRange(expanded, SignalConstants.KEY_LENGTH * 3, SignalConstants.KEY_LENGTH * 4); + var patchMacKey = Arrays.copyOfRange(expanded, SignalConstants.KEY_LENGTH * 4, expanded.length); return new MutationKeys(indexKey, encKey, macKey, snapshotMacKey, patchMacKey); } } diff --git a/src/main/java/it/auties/whatsapp/model/sync/MutationSync.java b/src/main/java/it/auties/whatsapp/model/sync/MutationSync.java index 42ccbde9b..1f6ccf5de 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/MutationSync.java +++ b/src/main/java/it/auties/whatsapp/model/sync/MutationSync.java @@ -1,15 +1,14 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("SyncdMutation") +@ProtobufMessage(name = "SyncdMutation") public record MutationSync( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) RecordSync.Operation operation, @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) RecordSync record -) implements ProtobufMessage, Syncable { +) implements Syncable { } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/sync/MutationsSync.java b/src/main/java/it/auties/whatsapp/model/sync/MutationsSync.java index c8c2e06ef..dd55bd77f 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/MutationsSync.java +++ b/src/main/java/it/auties/whatsapp/model/sync/MutationsSync.java @@ -1,16 +1,15 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.List; -@ProtobufMessageName("SyncdMutations") +@ProtobufMessage(name = "SyncdMutations") public record MutationsSync( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) List mutations -) implements ProtobufMessage { +) { } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/sync/PatchSync.java b/src/main/java/it/auties/whatsapp/model/sync/PatchSync.java index 275997608..cc40a91cc 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/PatchSync.java +++ b/src/main/java/it/auties/whatsapp/model/sync/PatchSync.java @@ -1,15 +1,14 @@ package it.auties.whatsapp.model.sync; import com.fasterxml.jackson.annotation.JsonCreator; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.List; -@ProtobufMessageName("SyncdPatch") -public final class PatchSync implements ProtobufMessage { +@ProtobufMessage(name = "SyncdPatch") +public final class PatchSync { @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) private VersionSync version; @ProtobufProperty(index = 2, type = ProtobufType.OBJECT) diff --git a/src/main/java/it/auties/whatsapp/model/sync/PatchType.java b/src/main/java/it/auties/whatsapp/model/sync/PatchType.java index 3d84e4a9b..c32dd917a 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/PatchType.java +++ b/src/main/java/it/auties/whatsapp/model/sync/PatchType.java @@ -1,13 +1,14 @@ package it.auties.whatsapp.model.sync; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.model.ProtobufEnum; import java.util.Arrays; import java.util.Locale; import java.util.NoSuchElementException; -public enum PatchType implements ProtobufEnum { +@ProtobufEnum +public enum PatchType { CRITICAL_BLOCK(0), CRITICAL_UNBLOCK_LOW(1), REGULAR_HIGH(2), diff --git a/src/main/java/it/auties/whatsapp/model/sync/PhotoChange.java b/src/main/java/it/auties/whatsapp/model/sync/PhotoChange.java index f46bd38f9..7644c3d81 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/PhotoChange.java +++ b/src/main/java/it/auties/whatsapp/model/sync/PhotoChange.java @@ -1,14 +1,13 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import static it.auties.protobuf.model.ProtobufType.BYTES; import static it.auties.protobuf.model.ProtobufType.UINT32; -@ProtobufMessageName("PhotoChange") +@ProtobufMessage(name = "PhotoChange") public record PhotoChange(@ProtobufProperty(index = 1, type = BYTES) byte[] oldPhoto, @ProtobufProperty(index = 2, type = BYTES) byte[] newPhoto, - @ProtobufProperty(index = 3, type = UINT32) Integer newPhotoId) implements ProtobufMessage { + @ProtobufProperty(index = 3, type = UINT32) Integer newPhotoId) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/PrimaryFeature.java b/src/main/java/it/auties/whatsapp/model/sync/PrimaryFeature.java index 638faf9c0..eb7a554f1 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/PrimaryFeature.java +++ b/src/main/java/it/auties/whatsapp/model/sync/PrimaryFeature.java @@ -1,16 +1,15 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.List; -@ProtobufMessageName("SyncActionValue.PrimaryFeature") +@ProtobufMessage(name = "SyncActionValue.PrimaryFeature") public record PrimaryFeature( @ProtobufProperty(index = 1, type = ProtobufType.STRING) List flags -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/PushName.java b/src/main/java/it/auties/whatsapp/model/sync/PushName.java index 8b9a6c6a6..8dcd6afc0 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/PushName.java +++ b/src/main/java/it/auties/whatsapp/model/sync/PushName.java @@ -1,14 +1,13 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import java.util.Optional; import static it.auties.protobuf.model.ProtobufType.STRING; -@ProtobufMessageName("Pushname") +@ProtobufMessage(name = "Pushname") public record PushName(@ProtobufProperty(index = 1, type = STRING) String id, - @ProtobufProperty(index = 2, type = STRING) Optional name) implements ProtobufMessage { + @ProtobufProperty(index = 2, type = STRING) Optional name) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/RecentEmojiWeight.java b/src/main/java/it/auties/whatsapp/model/sync/RecentEmojiWeight.java index 4b5fc032e..3d3928716 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/RecentEmojiWeight.java +++ b/src/main/java/it/auties/whatsapp/model/sync/RecentEmojiWeight.java @@ -1,13 +1,12 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import static it.auties.protobuf.model.ProtobufType.FLOAT; import static it.auties.protobuf.model.ProtobufType.STRING; -@ProtobufMessageName("RecentEmojiWeight") +@ProtobufMessage(name = "RecentEmojiWeight") public record RecentEmojiWeight(@ProtobufProperty(index = 1, type = STRING) String emoji, - @ProtobufProperty(index = 2, type = FLOAT) Float weight) implements ProtobufMessage { + @ProtobufProperty(index = 2, type = FLOAT) Float weight) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/RecordSync.java b/src/main/java/it/auties/whatsapp/model/sync/RecordSync.java index 49e89617c..2752802d2 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/RecordSync.java +++ b/src/main/java/it/auties/whatsapp/model/sync/RecordSync.java @@ -1,13 +1,12 @@ package it.auties.whatsapp.model.sync; +import it.auties.protobuf.annotation.ProtobufEnum; import it.auties.protobuf.annotation.ProtobufEnumIndex; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufEnum; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("SyncdRecord") +@ProtobufMessage(name = "SyncdRecord") public record RecordSync( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) IndexSync index, @@ -15,7 +14,7 @@ public record RecordSync( ValueSync value, @ProtobufProperty(index = 3, type = ProtobufType.OBJECT) KeyId keyId -) implements ProtobufMessage, Syncable { +) implements Syncable { @Override public Operation operation() { return Operation.SET; @@ -26,7 +25,8 @@ public RecordSync record() { return this; } - public enum Operation implements ProtobufEnum { + @ProtobufEnum + public enum Operation { SET(0, ((byte) (0x1))), REMOVE(1, ((byte) (0x2))); diff --git a/src/main/java/it/auties/whatsapp/model/sync/ServerErrorReceipt.java b/src/main/java/it/auties/whatsapp/model/sync/ServerErrorReceipt.java index 49594b94b..b37e7e633 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/ServerErrorReceipt.java +++ b/src/main/java/it/auties/whatsapp/model/sync/ServerErrorReceipt.java @@ -1,14 +1,13 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import static it.auties.protobuf.model.ProtobufType.STRING; -@ProtobufMessageName("ServerErrorReceipt") +@ProtobufMessage(name = "ServerErrorReceipt") public record ServerErrorReceipt( @ProtobufProperty(index = 1, type = STRING) String stanzaId -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/SnapshotSync.java b/src/main/java/it/auties/whatsapp/model/sync/SnapshotSync.java index 6be7e40c5..29d696dbe 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/SnapshotSync.java +++ b/src/main/java/it/auties/whatsapp/model/sync/SnapshotSync.java @@ -1,13 +1,12 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; import java.util.List; -@ProtobufMessageName("SyncdSnapshot") +@ProtobufMessage(name = "SyncdSnapshot") public record SnapshotSync( @ProtobufProperty(index = 1, type = ProtobufType.OBJECT) VersionSync version, @@ -17,6 +16,6 @@ public record SnapshotSync( byte[] mac, @ProtobufProperty(index = 4, type = ProtobufType.OBJECT) KeyId keyId -) implements ProtobufMessage { +) { } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/model/sync/StickerMetadata.java b/src/main/java/it/auties/whatsapp/model/sync/StickerMetadata.java index 72553c499..13e4e7520 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/StickerMetadata.java +++ b/src/main/java/it/auties/whatsapp/model/sync/StickerMetadata.java @@ -1,12 +1,11 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import static it.auties.protobuf.model.ProtobufType.*; -@ProtobufMessageName("StickerMetadata") +@ProtobufMessage(name = "StickerMetadata") public record StickerMetadata(@ProtobufProperty(index = 1, type = STRING) String url, @ProtobufProperty(index = 2, type = BYTES) byte[] fileSha256, @ProtobufProperty(index = 3, type = BYTES) byte[] fileEncSha256, @@ -17,5 +16,5 @@ public record StickerMetadata(@ProtobufProperty(index = 1, type = STRING) String @ProtobufProperty(index = 8, type = STRING) String directPath, @ProtobufProperty(index = 9, type = UINT64) long fileLength, @ProtobufProperty(index = 10, type = FLOAT) float weight, - @ProtobufProperty(index = 11, type = INT64) long lastStickerSentTs) implements ProtobufMessage { + @ProtobufProperty(index = 11, type = INT64) long lastStickerSentTs) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/SyncActionMessage.java b/src/main/java/it/auties/whatsapp/model/sync/SyncActionMessage.java index 00e449aa1..585b5e6e1 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/SyncActionMessage.java +++ b/src/main/java/it/auties/whatsapp/model/sync/SyncActionMessage.java @@ -1,14 +1,13 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.whatsapp.model.message.model.ChatMessageKey; import static it.auties.protobuf.model.ProtobufType.INT64; import static it.auties.protobuf.model.ProtobufType.OBJECT; -@ProtobufMessageName("SyncActionMessage") +@ProtobufMessage(name = "SyncActionMessage") public record SyncActionMessage(@ProtobufProperty(index = 1, type = OBJECT) ChatMessageKey key, - @ProtobufProperty(index = 2, type = INT64) Long timestamp) implements ProtobufMessage { + @ProtobufProperty(index = 2, type = INT64) Long timestamp) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/ValueSync.java b/src/main/java/it/auties/whatsapp/model/sync/ValueSync.java index abf99f2f9..6271043c3 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/ValueSync.java +++ b/src/main/java/it/auties/whatsapp/model/sync/ValueSync.java @@ -1,14 +1,13 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("SyncdValue") +@ProtobufMessage(name = "SyncdValue") public record ValueSync( @ProtobufProperty(index = 1, type = ProtobufType.BYTES) byte[] blob -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/model/sync/VersionSync.java b/src/main/java/it/auties/whatsapp/model/sync/VersionSync.java index d3d072cd6..6b32611c9 100644 --- a/src/main/java/it/auties/whatsapp/model/sync/VersionSync.java +++ b/src/main/java/it/auties/whatsapp/model/sync/VersionSync.java @@ -1,14 +1,13 @@ package it.auties.whatsapp.model.sync; -import it.auties.protobuf.annotation.ProtobufMessageName; +import it.auties.protobuf.annotation.ProtobufMessage; import it.auties.protobuf.annotation.ProtobufProperty; -import it.auties.protobuf.model.ProtobufMessage; import it.auties.protobuf.model.ProtobufType; -@ProtobufMessageName("SyncdVersion") +@ProtobufMessage(name = "SyncdVersion") public record VersionSync( @ProtobufProperty(index = 1, type = ProtobufType.UINT64) Long version -) implements ProtobufMessage { +) { } diff --git a/src/main/java/it/auties/whatsapp/net/HttpClient.java b/src/main/java/it/auties/whatsapp/net/HttpClient.java new file mode 100644 index 000000000..28e7e56dd --- /dev/null +++ b/src/main/java/it/auties/whatsapp/net/HttpClient.java @@ -0,0 +1,676 @@ +package it.auties.whatsapp.net; + +import it.auties.whatsapp.util.Bytes; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLParameters; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.InetSocketAddress; +import java.net.StandardSocketOptions; +import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.zip.GZIPInputStream; + +@SuppressWarnings("unused") +public class HttpClient implements AutoCloseable { + private static final int REQUEST_TIMEOUT = 300; + private static final String[] IOS_CIPHERS = { + "TLS_AES_128_GCM_SHA256", + "TLS_CHACHA20_POLY1305_SHA256", + "TLS_AES_256_GCM_SHA384", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_RSA_WITH_AES_256_GCM_SHA384", + "TLS_RSA_WITH_AES_128_CBC_SHA", + "TLS_RSA_WITH_AES_256_CBC_SHA", + "TLS_EMPTY_RENEGOTIATION_INFO_SCSV" + }; + private static final String[] ANDROID_CIPHERS = { + "TLS_AES_128_GCM_SHA256" + //,"use default" + }; + public static final byte[] EMPTY_BUFFER = new byte[0]; + private static final byte[] HTTP_MESSAGE_END_BYTES = "\r\n\r\n".getBytes(StandardCharsets.US_ASCII); + + private final Platform platform; + private final URI proxy; + private final ConcurrentMap aliveSockets; + public HttpClient(Platform platform) { + this(platform, null); + } + + public HttpClient(Platform platform, boolean allowKeepAlive) { + this(platform, null, allowKeepAlive); + } + + public HttpClient(Platform platform, URI proxy) { + this(platform, proxy, true); + } + + public HttpClient(Platform platform, URI proxy, boolean allowKeepAlive) { + this.proxy = proxy; + this.platform = platform; + this.aliveSockets = allowKeepAlive ? new ConcurrentHashMap<>() : null; + } + + public static String toFormParams(Map values) { + return values.entrySet() + .stream() + .map(entry -> entry.getKey() + "=" + entry.getValue()) + .collect(Collectors.joining("&")); + } + + public static Map parseFormParams(String params) { + return Arrays.stream(params.split("&")) + .map(entry -> entry.split("=", 2)) + .collect(Collectors.toUnmodifiableMap(entry -> entry[0], entry -> entry[1])); + } + + public CompletableFuture getRaw(URI uri) { + return sendRequest("GET", uri, null, null); + } + + public CompletableFuture getRaw(URI uri, Map headers) { + return sendRequest("GET", uri, headers, null); + } + + public CompletableFuture getString(URI uri) { + return sendRequest("GET", uri, null, null) + .thenApplyAsync(String::new); + } + + public CompletableFuture getString(URI uri, Map headers) { + return sendRequest("GET", uri, headers, null) + .thenApplyAsync(String::new); + } + + public CompletableFuture postRaw(URI uri, Map headers, byte[] body) { + return sendRequest("POST", uri, headers, body); + } + + public CompletableFuture postRawWithoutSslParams(URI uri, Map headers, byte[] body) { + return sendRequest("POST", uri, headers, body); + } + + private CompletableFuture sendRequest(String method, URI uri, Map headers, byte[] body) { + var socket = getLockableSocketClient(uri); + var builder = createRequestPayload(method, uri, headers, body); + return (socket.isConnected() ? CompletableFuture.completedFuture(null) : socket.connectAsync(toSocketAddress(uri))) + .thenComposeAsync(ignored -> socket.writeAsync(builder.toString().getBytes())) + .thenComposeAsync(ignored -> readResponse(method, uri, headers, body, socket)) + .orTimeout(REQUEST_TIMEOUT, TimeUnit.SECONDS) + .exceptionallyComposeAsync(error -> { + closeSocketSilently(uri, socket); + return CompletableFuture.failedFuture(error); + }); + } + + private void closeSocketSilently(URI uri, SocketClient socket) { + try { + socket.close(); + if(aliveSockets != null) { + aliveSockets.remove(uri.getHost() + ":" + uri.getPort(), socket); + } + } catch (Throwable ignored) { + + } + } + + private void closeSocketSilently(URI uri, PartialResponseInfo info, SocketClient socket) { + if (aliveSockets == null || info.closeConnection()) { + closeSocketSilently(uri, socket); + } + } + + private CompletableFuture readResponse(String method, URI uri, Map headers, byte[] body, SocketClient socket) { + var info = new PartialResponseInfo(); + return readPartialResponse(method, uri, headers, body, info, socket); + } + + private CompletableFuture readPartialResponse(String method, URI uri, Map headers, byte[] body, PartialResponseInfo info, SocketClient socket) { + return socket.readAsync(readReceiveBufferSize(socket)).thenComposeAsync(responseBuffer -> { + info.updateSource(responseBuffer, true); + return handlePartialResponse(method, uri, headers, body, info, socket); + }); + } + + private CompletableFuture handlePartialResponse(String method, URI uri, Map headers, byte[] body, PartialResponseInfo info, SocketClient socket) { + var partial = handleStatusCodeAndHeaders(info); + if (partial) { + return readPartialResponse(method, uri, headers, body, info, socket); + } + + if (info.isRedirect()) { + var location = URI.create(Objects.requireNonNull(info.location(), "Missing location for redirect status code")); + return sendRequest(method, location.isAbsolute() ? location : uri.resolve(location), headers, body); + } + + if (!String.valueOf(info.statusCode()).startsWith("20")) { + throw new IllegalArgumentException("Unexpected response status code: " + info.statusCode()); + } + + if(info.contentLength() == 0) { + return CompletableFuture.completedFuture(EMPTY_BUFFER); + } + + var result = info.contentLength() == -1 ? readChunkedResponse(info, socket) : readFullResponse(uri, info, socket); + return result.thenApplyAsync(response -> decodeResponse(info, response)); + } + + private byte[] decodeResponse(PartialResponseInfo info, byte[] response) { + if(info.contentEncoding().isEmpty()) { + return response; + } + + for (var contentEncoding : info.contentEncoding()) { + response = decodeResponse(response, contentEncoding); + } + + return response; + } + + private byte[] decodeResponse(byte[] response, String contentEncoding) { + return switch (contentEncoding.toLowerCase()) { + case "gzip" -> { + try (var input = new GZIPInputStream(new ByteArrayInputStream(response))) { + yield input.readAllBytes(); + } catch (IOException exception) { + throw new UncheckedIOException("Cannot decode gzip encoded response", exception); + } + } + default -> throw new IllegalArgumentException("Unsupported content encoding: " + contentEncoding); + }; + } + + private CompletableFuture readFullResponse(URI uri, PartialResponseInfo info, SocketClient socket) { + var partialBody = info.readBody(info.contentLength()); + if(partialBody != null) { + closeSocketSilently(uri, info, socket); + return CompletableFuture.completedFuture(partialBody); + } + + return socket.readFullyAsync(info.contentLength() - info.remaining()) + .thenApplyAsync(additionalBody -> concatFullResponse(uri, info, socket, additionalBody)) + .exceptionallyComposeAsync(error -> handleFullResponseError(uri, info, socket, error)); + } + + private byte[] concatFullResponse(URI uri, PartialResponseInfo info, SocketClient socket, ByteBuffer additionalBody) { + closeSocketSilently(uri, info, socket); + var remaining = info.remaining(); + var result = new byte[remaining + additionalBody.remaining()]; + info.readBody(result); + additionalBody.get(result, remaining, additionalBody.remaining()); + return result; + } + + private CompletableFuture handleFullResponseError(URI uri, PartialResponseInfo info, SocketClient socket, Throwable error) { + closeSocketSilently(uri, info, socket); + return CompletableFuture.failedFuture(error); + } + + private boolean handleStatusCodeAndHeaders(PartialResponseInfo info) { + while (info.hasNext()) { + var responseLine = info.readHeaderLine(); + info.setLastHeaderLineIndex(info.currentHeaderLineIndex()); + if(info.statusCode() == -1) { + if(!responseLine.startsWith("HTTP")) { + continue; + } + + info.setStatusCode(parseStatusCode(responseLine)); + continue; + } + + if (responseLine.isEmpty()) { + info.finish(); + break; + } + + var responseLineParts = responseLine.split(":", 2); + var headerKey = responseLineParts[0]; + var headerValue = responseLineParts.length == 2 ? responseLineParts[1].trim() : ""; + switch (headerKey.toLowerCase()) { + case "content-length" -> { + try { + info.setContentLength(Integer.parseUnsignedInt(headerValue)); + } catch (NumberFormatException exception) { + throw new IllegalArgumentException("Malformed Content-Length header: " + responseLine); + } + } + case "connection" -> info.setCloseConnection(headerValue.equalsIgnoreCase("close")); + case "location" -> info.setLocation(headerValue); + case "transfer-encoding" -> info.transferEncoding().addAll(Arrays.stream(headerValue.split(",")).map(TransferEncoding::of).toList()); + case "content-encoding" -> info.contentEncoding().addAll(Arrays.stream(headerValue.split(",")).map(String::trim).toList()); + } + } + return info.isPartial(); + } + + private int parseStatusCode(String responseLine) { + var responseStatusParts = responseLine.split(" "); + if (responseStatusParts.length < 2) { + throw new IllegalArgumentException("Unexpected response status code: " + responseLine); + } + + var statusCode = responseStatusParts[1]; + try { + return Integer.parseUnsignedInt(statusCode); + } catch (NumberFormatException exception) { + throw new IllegalArgumentException("Malformed status code: " + responseLine); + } + } + + private CompletableFuture readChunkedResponse(PartialResponseInfo info, SocketClient socket) { + var chunkedLength = info.readChunkedBodyLength(); + if(chunkedLength.truncated()) { + return socket.readAsync(readReceiveBufferSize(socket)).thenComposeAsync(responseBuffer -> { + info.updateSource(responseBuffer, false); + return readChunkedResponse(info, socket); + }); + } + + if(chunkedLength.value() == -1 || !info.isPartial()) { + return CompletableFuture.completedFuture(EMPTY_BUFFER); + } + + return readChunkContent(info, socket, chunkedLength.value()).thenComposeAsync(currentChunk -> { + if(!info.isPartial()) { + return CompletableFuture.completedFuture(currentChunk); + } + + return readChunkedResponse(info, socket) + .thenApplyAsync(nextChunk -> Bytes.concat(currentChunk, nextChunk)); + }); + } + + private CompletableFuture readChunkContent(PartialResponseInfo info, SocketClient socket, int chunkedLength) { + if(info.remaining() >= chunkedLength + 2) { + var result = info.readBody(chunkedLength); + info.checkChunkTrailing(); + return CompletableFuture.completedFuture(result); + } + + return socket.readFullyAsync(chunkedLength - info.remaining() + 2).thenComposeAsync(responseBuffer -> { + info.updateSource(responseBuffer, false); + return readChunkContent(info, socket, chunkedLength); + }); + } + + private static final class PartialResponseInfo { + private String headers; + private ByteBuffer body; + private int statusCode; + private int contentLength; + private final List contentEncoding; + private boolean closeConnection; + private String location; + private int lastHeaderLineIndex; + private int currentHeaderLineIndex; + private boolean partial; + private final List transferEncoding; + + private PartialResponseInfo() { + this.headers = null; + this.body = null; + this.statusCode = -1; + this.contentLength = -1; + this.closeConnection = false; + this.location = null; + this.lastHeaderLineIndex = -1; + this.currentHeaderLineIndex = -1; + this.partial = true; + this.transferEncoding = new ArrayList<>(); + this.contentEncoding = new ArrayList<>(); + } + + public boolean isPartial() { + return partial; + } + + private boolean isRedirect() { + return statusCode == 302; + } + + public int statusCode() { + return statusCode; + } + + public void setStatusCode(Integer statusCode) { + this.statusCode = statusCode; + } + + private int contentLength() { + return contentLength; + } + + public List contentEncoding() { + return contentEncoding; + } + + private boolean closeConnection() { + return closeConnection; + } + + private String location() { + return location; + } + + public List transferEncoding() { + return transferEncoding; + } + + private int currentHeaderLineIndex() { + return currentHeaderLineIndex; + } + + private void setContentLength(int contentLength) { + this.contentLength = contentLength; + } + + private void setCloseConnection(boolean closeConnection) { + this.closeConnection = closeConnection; + } + + private void setLastHeaderLineIndex(int lastHeaderLineIndex) { + this.lastHeaderLineIndex = lastHeaderLineIndex; + } + + private void setLocation(String location) { + this.location = location; + } + + private void finish() { + this.partial = false; + } + + private boolean hasNext() { + return headers != null && (currentHeaderLineIndex = headers.indexOf("\n", lastHeaderLineIndex + 1)) != -1; + } + + private void updateSource(ByteBuffer response, boolean headers) { + if(headers) { + var divider = getMessageContentDivider(response); + var oldLimit = response.limit(); + if(divider != -1) { + response.limit(divider); + } + var content = StandardCharsets.US_ASCII.decode(response).toString(); + if(this.headers == null) { + this.headers = content; + }else { + this.headers = this.headers + content; + } + response.limit(oldLimit); + } + + if(body != null && body.hasRemaining()) { + var result = new byte[body.remaining() + response.remaining()]; + var i = 0; + while (body.hasRemaining()) { + result[i++] = body.get(); + } + while (response.hasRemaining()) { + result[i++] = response.get(); + } + this.body = ByteBuffer.wrap(result); + }else { + this.body = response; + } + } + + private int getMessageContentDivider(ByteBuffer partialResult) { + var index = -1; + for (int i = 0; i < partialResult.remaining() - HTTP_MESSAGE_END_BYTES.length; i++) { + if(partialResult.get(i) == HTTP_MESSAGE_END_BYTES[0] + && partialResult.get(i + 1) == HTTP_MESSAGE_END_BYTES[1] + && partialResult.get(i + 2) == HTTP_MESSAGE_END_BYTES[2] + && partialResult.get(i + 3) == HTTP_MESSAGE_END_BYTES[3]) { + index = i + HTTP_MESSAGE_END_BYTES.length; + break; + } + } + return index; + } + + private String readHeaderLine() { + return headers.substring(lastHeaderLineIndex + 1, currentHeaderLineIndex).trim(); + } + + private byte[] readBody(int length) { + if (body.remaining() < length) { + return null; + } + + var result = new byte[length]; + body.get(result); + return result; + } + + private void readBody(byte[] destination) { + if(!body.hasRemaining()) { + return; + } + + body.get(destination, 0, body.remaining()); + } + + public ChunkedResult readChunkedBodyLength() { + var position = body.position(); + var chunkSizeDigitsCount = 0; + while (position + chunkSizeDigitsCount + 1 >= body.limit() + || body.get(position + chunkSizeDigitsCount) != '\r' + || body.get(position + chunkSizeDigitsCount + 1) != '\n') { + if(position + chunkSizeDigitsCount + 1 >= body.limit()) { + return new ChunkedResult(-1, transferEncoding.contains(TransferEncoding.CHUNKED)); + } + + chunkSizeDigitsCount++; + } + + var chunkSize = 0; + for (var i = 1; i <= chunkSizeDigitsCount; i++) { + chunkSize += (int) (Character.getNumericValue(body.get()) * Math.pow(16, chunkSizeDigitsCount - i)); + } + + checkChunkTrailing(); + + this.partial = chunkSize != 0; + return new ChunkedResult(chunkSize, false); + } + + private void checkChunkTrailing() { + if(body.get() != '\r' || body.get() != '\n') { + throw new IllegalArgumentException("Truncated chunked message size"); + } + } + + private int remaining() { + return body.remaining(); + } + } + + private record ChunkedResult(int value, boolean truncated) { + + } + + private enum TransferEncoding { + CHUNKED, + COMPRESS, + GZIP, + DEFLATE, + UNKNOWN; + + private static final Map CASES = Map.of( + "chunked", CHUNKED, + "compress", COMPRESS, + "gzip", GZIP, + "deflate", DEFLATE + ); + + private static TransferEncoding of(String value) { + return CASES.getOrDefault(value.toLowerCase().trim(), UNKNOWN); + } + } + + private StringBuilder createRequestPayload(String method, URI uri, Map headers, byte[] body) { + var builder = new StringBuilder(); + builder.append(method) + .append(" ") + .append(uri.getPath()) + .append(uri.getQuery() == null || uri.getQuery().isEmpty() ? "" : "?" + uri.getQuery()) + .append(" HTTP/1.1\r\n"); + builder.append("Host: ") + .append(uri.getHost()) + .append(uri.getPort() == -1 ? "" : ":" + uri.getPort()) + .append("\r\n"); + if(platform == Platform.DEFAULT && (headers == null || headers.keySet().stream().noneMatch(entry -> entry.equalsIgnoreCase("User-Agent")))) { + builder.append("User-Agent: Java/%s\r\n".formatted(Runtime.version().feature())); + } + + if(headers != null) { + headers.forEach((headerKey, headerValue) -> builder.append(headerKey.trim()) + .append(": ") + .append(headerValue) + .append("\r\n")); + } + if (body != null) { + builder.append("Content-Length: ") + .append(body.length) + .append("\r\n"); + } + builder.append("\r\n"); + if (body != null) { + builder.append(new String(body, StandardCharsets.ISO_8859_1)) + .append("\r\n"); + } + return builder; + } + + private InetSocketAddress toSocketAddress(URI uri) { + var hostname = uri.getHost(); + var port = uri.getPort() != -1 ? uri.getPort() : switch (uri.getScheme().toLowerCase()) { + case "https" -> 443; + case "http" -> 80; + default -> throw new IllegalStateException("Unexpected scheme: " + uri.getScheme().toLowerCase()); + }; + return proxy == null ? new InetSocketAddress(hostname, port) : InetSocketAddress.createUnresolved(hostname, port); + } + + private int readReceiveBufferSize(SocketClient client) { + try { + return client.getOption(StandardSocketOptions.SO_RCVBUF); + }catch (IOException exception) { + return 8192; + } + } + + private SocketClient getLockableSocketClient(URI uri) { + try { + var aliveSocket = aliveSockets == null ? null : aliveSockets.get(uri.getHost() + ":" + uri.getPort()); + if(aliveSocket != null) { + return aliveSocket; + } + + return switch (uri.getScheme().toLowerCase()) { + case "http" -> { + var result = SocketClient.newPlainClient(proxy); + result.setKeepAlive(true); + if(aliveSockets != null) { + aliveSockets.put(uri.getHost() + ":" + uri.getPort(), result); + } + yield result; + } + case "https" -> { + var sslEngine = platform.sslContext() + .createSSLEngine(uri.getHost(), uri.getPort() == 1 ? 443 : uri.getPort()); + sslEngine.setUseClientMode(true); + platform.sslParameters() + .ifPresent(sslEngine::setSSLParameters); + var result = SocketClient.newSecureClient(sslEngine, proxy); + result.setKeepAlive(true); + if(aliveSockets != null) { + aliveSockets.put(uri.getHost() + ":" + uri.getPort(), result); + } + yield result; + } + default -> throw new IllegalStateException("Unexpected scheme: " + uri.getScheme().toLowerCase()); + }; + }catch (IOException exception) { + throw new UncheckedIOException(exception); + } + } + + public enum Platform { + DEFAULT, + IOS, + ANDROID; + + public SSLContext sslContext() { + return DEFAULT_SSL_CONTEXT; + } + + private Optional sslParameters() { + return switch (this) { + case DEFAULT -> Optional.empty(); + case IOS -> { + var sslParameters = sslContext().getDefaultSSLParameters(); + sslParameters.setCipherSuites(IOS_CIPHERS); + sslParameters.setUseCipherSuitesOrder(true); + yield Optional.of(sslParameters); + } + case ANDROID -> { + var sslParameters = sslContext().getDefaultSSLParameters(); + sslParameters.setCipherSuites(ANDROID_CIPHERS); + sslParameters.setUseCipherSuitesOrder(true); + yield Optional.of(sslParameters); + } + }; + } + + private static final SSLContext DEFAULT_SSL_CONTEXT; + static { + try { + var sslContext = SSLContext.getInstance("TLSv1.3"); + sslContext.init(null, null, new SecureRandom()); + DEFAULT_SSL_CONTEXT = sslContext; + }catch (Throwable throwable) { + throw new RuntimeException(throwable); + } + } + } + + @Override + public void close() { + if(aliveSockets == null) { + return; + } + + for(var socket : aliveSockets.values()) { + try { + socket.close(); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + aliveSockets.clear(); + } +} diff --git a/src/main/java/it/auties/whatsapp/net/Response.java b/src/main/java/it/auties/whatsapp/net/Response.java new file mode 100644 index 000000000..8bbc956d8 --- /dev/null +++ b/src/main/java/it/auties/whatsapp/net/Response.java @@ -0,0 +1,34 @@ +package it.auties.whatsapp.net; + +import java.util.concurrent.CompletableFuture; + +public sealed interface Response { + default boolean complete(V result) { + switch (this) { + case Callback callback -> callback.onResult(result, null); + case Future future -> future.complete(result); + } + + return true; + } + + @SuppressWarnings("UnusedReturnValue") // Would violate the return value of CompletableFuture + default boolean completeExceptionally(Throwable throwable) { + switch (this) { + case Callback callback -> callback.onResult(null, throwable); + case Future future -> future.completeExceptionally(throwable); + } + + return true; + } + + + final class Future extends CompletableFuture implements Response { + + } + + @FunctionalInterface + non-sealed interface Callback extends Response { + void onResult(V result, Throwable error); + } +} diff --git a/src/main/java/it/auties/whatsapp/net/SocketClient.java b/src/main/java/it/auties/whatsapp/net/SocketClient.java new file mode 100644 index 000000000..562ff2169 --- /dev/null +++ b/src/main/java/it/auties/whatsapp/net/SocketClient.java @@ -0,0 +1,1216 @@ +package it.auties.whatsapp.net; + +import it.auties.whatsapp.util.Proxies; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; +import javax.net.ssl.SSLEngineResult.Status; +import javax.net.ssl.SSLException; +import java.io.*; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.InvalidMarkException; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Set; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicBoolean; + +@SuppressWarnings("unused") +public class SocketClient extends Socket implements AutoCloseable { + private static final int DEFAULT_CONNECTION_TIMEOUT = 300; + + public static SocketClient newPlainClient(URI proxy) throws IOException { + var channel = AsynchronousSocketChannel.open(); + var layerSupport = new SocketTransport.Plain(channel); + var proxySupport = SocketConnection.of(channel, layerSupport, proxy); + return new SocketClient(channel, proxySupport, layerSupport); + } + + public static SocketClient newSecureClient(SSLEngine sslEngine, URI proxy) throws IOException { + var channel = AsynchronousSocketChannel.open(); + var layerSupport = new SocketTransport.Secure(channel, sslEngine); + var proxySupport = SocketConnection.of(channel, layerSupport, proxy); + return new SocketClient(channel, proxySupport, layerSupport); + } + + final AsynchronousSocketChannel channel; + final SocketConnection socketConnection; + SocketTransport socketTransport; + private SocketClient(AsynchronousSocketChannel channel, SocketConnection socketConnection, SocketTransport socketTransport) { + this.channel = channel; + this.socketConnection = socketConnection; + this.socketTransport = socketTransport; + } + + public CompletableFuture upgradeToSsl(SSLEngine sslEngine) { + if(!isConnected()) { + throw new IllegalArgumentException("The socket is not connected"); + } + + if(socketTransport.isSecure()) { + throw new IllegalStateException("This socket is already using a secure connection"); + } + + this.socketTransport = new SocketTransport.Secure(channel, sslEngine); + return socketTransport.handshake(); // Upgrading a websocket is not supported, so path is always null + } + + @Override + public void connect(SocketAddress endpoint) throws IOException { + connect(endpoint, DEFAULT_CONNECTION_TIMEOUT); + } + + @Override + public void connect(SocketAddress endpoint, int timeout) throws IOException { + if(!(endpoint instanceof InetSocketAddress inetSocketAddress)) { + throw new IllegalArgumentException("Unsupported address type"); + } + + var future = connectAsync(inetSocketAddress, timeout); + future.join(); + } + + public CompletableFuture connectAsync(InetSocketAddress address) { + return connectAsync(address, DEFAULT_CONNECTION_TIMEOUT); + } + + public CompletableFuture connectAsync(InetSocketAddress address, int timeout) { + return socketConnection.connectAsync(address, timeout) + .thenComposeAsync(ignored -> socketTransport.handshake()) + .exceptionallyComposeAsync(this::closeSocketOnError); + } + + private CompletableFuture closeSocketOnError(Throwable error) { + try { + close(); + }catch (Throwable ignored) { + + } + + return CompletableFuture.failedFuture(error); + } + + @Override + public InputStream getInputStream() throws IOException { + if(!isConnected()) { + throw new IOException("Connection is closed"); + } + + return new InputStream() { + @Override + public int read() throws IOException { + var data = new byte[1]; + var result = read(data); + if(result == -1) { + close(); + throw new EOFException(); + } + + return Byte.toUnsignedInt(data[0]); + } + + @Override + public int read(byte[] b) { + return read(b, 0, b.length); + } + + @Override + public int read(byte[] b, int off, int len) { + if(len == 0) { + return 0; + } + + return readAsync(ByteBuffer.wrap(b, off, len)) + .join(); + } + + @Override + public void close() throws IOException { + SocketClient.this.close(); + } + }; + } + + @Override + public OutputStream getOutputStream() throws IOException { + if(!isConnected()) { + throw new IOException("Connection is closed"); + } + + return new OutputStream() { + @Override + public void write(int b) { + write(new byte[]{(byte) b}, 0, 1); + } + + @Override + public void write(byte[] b, int off, int len) { + writeAsync(b, off, len).join(); + } + + @Override + public void close() throws IOException { + SocketClient.this.close(); + } + }; + } + + @Override + public void close() throws IOException { + channel.close(); + } + + @Override + public boolean isBound() { + return channel.isOpen(); + } + + @Override + public boolean isConnected() { + try { + return channel.getRemoteAddress() != null; + } catch (IOException e) { + return false; + } + } + + @Override + public boolean isOutputShutdown() { + return !isConnected(); + } + + @Override + public boolean isInputShutdown() { + return !isConnected(); + } + + @Override + public boolean isClosed() { + return !channel.isOpen(); + } + + @Override + public int getReceiveBufferSize() { + try { + return channel.getOption(StandardSocketOptions.SO_RCVBUF); + } catch (Throwable e) { + return 0; + } + } + + @Override + public int getSendBufferSize() { + try { + return channel.getOption(StandardSocketOptions.SO_SNDBUF); + } catch (Throwable e) { + return 0; + } + } + + @Override + public void bind(SocketAddress endpoint) throws IOException { + throw new UnsupportedOperationException("Client socket"); + } + + @Override + public int getPort() { + try { + if(channel.getRemoteAddress() instanceof InetSocketAddress inetSocketAddress) { + return inetSocketAddress.getPort(); + } + + return -1; + } catch (Throwable e) { + return -1; + } + } + + @Override + public int getLocalPort() { + return -1; + } + + @Override + public InetAddress getInetAddress() { + try { + if(channel.getRemoteAddress() instanceof InetSocketAddress inetSocketAddress) { + return inetSocketAddress.getAddress(); + } + + return null; + } catch (Throwable e) { + return null; + } + } + + @Override + public InetAddress getLocalAddress() { + return null; + } + + @Override + public SocketAddress getRemoteSocketAddress() { + try { + return channel.getRemoteAddress(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public SocketAddress getLocalSocketAddress() { + return null; + } + + @Override + public void setTcpNoDelay(boolean on) throws SocketException { + if(!supportedOptions().contains(StandardSocketOptions.TCP_NODELAY)) { + return; + } + + try { + channel.setOption(StandardSocketOptions.TCP_NODELAY, on); + } catch (IOException e) { + throw new SocketException(e); + } + } + + @Override + public boolean getTcpNoDelay() throws SocketException { + try { + return getOption(StandardSocketOptions.TCP_NODELAY); + } catch (IOException e) { + throw new SocketException(e); + } + } + + @Override + public void setSoLinger(boolean on, int linger) throws SocketException { + if(!supportedOptions().contains(StandardSocketOptions.SO_LINGER)) { + return; + } + + try { + if(on) { + channel.setOption(StandardSocketOptions.SO_LINGER, linger); + }else { + channel.setOption(StandardSocketOptions.SO_LINGER, -1); + } + }catch (IOException e) { + throw new SocketException(e); + } + } + + @Override + public int getSoLinger() { + try { + return getOption(StandardSocketOptions.SO_LINGER); + } catch (Throwable ignored) { + return 0; + } + } + + @Override + public void sendUrgentData(int data) throws SocketException { + try { + var future = writeAsync(new byte[]{(byte) data}); + future.join(); + }catch (Throwable throwable) { + throw new SocketException(throwable); + } + } + + @Override + public void setOOBInline(boolean on) { + + } + + @Override + public boolean getOOBInline() { + return false; + } + + @Override + public void setSoTimeout(int timeout) { + + } + + @Override + public int getSoTimeout() { + return 0; + } + + @Override + public void setSendBufferSize(int size) throws SocketException { + if(!supportedOptions().contains(StandardSocketOptions.SO_SNDBUF)) { + return; + } + + try { + channel.setOption(StandardSocketOptions.SO_SNDBUF, size); + } catch (IOException e) { + throw new SocketException(e); + } + } + + @Override + public void setReceiveBufferSize(int size) throws SocketException { + if(!supportedOptions().contains(StandardSocketOptions.SO_RCVBUF)) { + return; + } + + try { + channel.setOption(StandardSocketOptions.SO_RCVBUF, size); + } catch (IOException e) { + throw new SocketException(e); + } + } + + @Override + public void setKeepAlive(boolean on) throws SocketException { + if(!supportedOptions().contains(StandardSocketOptions.SO_KEEPALIVE)) { + return; + } + + try { + channel.setOption(StandardSocketOptions.SO_KEEPALIVE, on); + } catch (IOException e) { + throw new SocketException(e); + } + } + + @Override + public boolean getKeepAlive() { + try { + return getOption(StandardSocketOptions.SO_KEEPALIVE); + } catch (IOException ignored) { + return false; + } + } + + @Override + public void setTrafficClass(int tc) throws SocketException { + if(!supportedOptions().contains(StandardSocketOptions.IP_TOS)) { + return; + } + + try { + channel.setOption(StandardSocketOptions.IP_TOS, tc); + } catch (IOException e) { + throw new SocketException(e); + } + } + + @Override + public int getTrafficClass() throws SocketException { + try { + return getOption(StandardSocketOptions.IP_TOS); + } catch (IOException e) { + throw new SocketException(e); + } + } + + @Override + public void setReuseAddress(boolean on) throws SocketException { + if(!supportedOptions().contains(StandardSocketOptions.SO_REUSEADDR)) { + return; + } + + try { + channel.setOption(StandardSocketOptions.SO_REUSEADDR, on); + } catch (IOException e) { + throw new SocketException(e); + } + } + + @Override + public boolean getReuseAddress() { + try { + return getOption(StandardSocketOptions.SO_REUSEADDR); + } catch (IOException ignored) { + return false; + } + } + + @Override + public void shutdownInput() throws IOException { + channel.shutdownInput(); + } + + @Override + public void shutdownOutput() throws IOException { + channel.shutdownOutput(); + } + + @Override + public Socket setOption(SocketOption name, T value) throws IOException { + channel.setOption(name, value); + return this; + } + + @Override + public T getOption(SocketOption name) throws IOException { + return channel.getOption(name); + } + + @Override + public Set> supportedOptions() { + return channel.supportedOptions(); + } + + public CompletableFuture writeAsync(byte[] data) { + return writeAsync(data, 0, data.length); + } + + public CompletableFuture writeAsync(byte[] data, int offset, int length) { + return writeAsync(ByteBuffer.wrap(data, offset, length)); + } + + public CompletableFuture writeAsync(ByteBuffer buffer) { + var future = new Response.Future(); + return socketTransport.write(buffer, future); + } + + public void readFullyAsync(int length, Response.Callback callback) { + if (length < 0) { + throw new IllegalArgumentException("Cannot read %s bytes from socket".formatted(length)); + } + + var buffer = ByteBuffer.allocate(length); + socketTransport.readFully(buffer, callback); + } + + public CompletableFuture readFullyAsync(int length) { + if (length < 0) { + return CompletableFuture.failedFuture(new IllegalArgumentException("Cannot read %s bytes from socket".formatted(length))); + } + + var buffer = ByteBuffer.allocate(length); + var future = new Response.Future(); + socketTransport.readFully(buffer, future); + return future; + } + + public CompletableFuture readAsync(int length) { + var future = new Response.Future(); + return readAsyncBuffer(length, future); + } + + public void readAsync(int length, Response.Callback callback) { + readAsyncBuffer(length, callback); + } + + private > R readAsyncBuffer(int length, R result) { + if (length < 0) { + result.completeExceptionally(new IllegalArgumentException("Cannot read %s bytes from socket".formatted(length))); + return result; + } + + var buffer = ByteBuffer.allocate(length); + readAsync(buffer, (bytesRead, error) -> { + if(error != null) { + result.completeExceptionally(error); + return; + } + + result.complete(buffer); + }); + return result; + } + + public CompletableFuture readAsync(ByteBuffer buffer) { + var future = new Response.Future(); + return socketTransport.read(buffer, true, future); + } + + public void readAsync(ByteBuffer buffer, Response.Callback callback) { + socketTransport.read(buffer, true, callback); + } + + private static sealed abstract class SocketTransport { + final AsynchronousSocketChannel channel; + private SocketTransport(AsynchronousSocketChannel channel) { + this.channel = channel; + } + + abstract CompletableFuture handshake(); + + abstract boolean isSecure(); + + abstract > R write(ByteBuffer buffer, R result); + + abstract > R read(ByteBuffer buffer, boolean lastRead, R result); + + > R readPlain(ByteBuffer buffer, boolean lastRead, R result) { + var outerCaller = new RuntimeException(); + channel.read(buffer, null, new CompletionHandler<>() { + @Override + public void completed(Integer bytesRead, Object attachment) { + if(bytesRead == -1) { + var eof = new EOFException(); + eof.addSuppressed(outerCaller); + result.completeExceptionally(eof); + return; + } + + if(lastRead) { + buffer.flip(); + } + + result.complete(bytesRead); + } + + @Override + public void failed(Throwable exc, Object attachment) { + exc.addSuppressed(outerCaller); + result.completeExceptionally(exc); + } + }); + return result; + } + + > R writePlain(ByteBuffer buffer, R result) { + var outerCaller = new RuntimeException(); + channel.write(buffer, null, new CompletionHandler<>() { + @Override + public void completed(Integer bytesWritten, Object attachment) { + if(bytesWritten == -1) { + result.completeExceptionally(new SocketException()); + return; + } + + if(buffer.hasRemaining()) { + writePlain(buffer, result); + return; + } + + result.complete(null); + } + + @Override + public void failed(Throwable exc, Object attachment) { + exc.addSuppressed(outerCaller); + result.completeExceptionally(exc); + } + }); + return result; + } + + public void readFully(ByteBuffer buffer, Response result) { + read(buffer, false, (Response.Callback) (readResult, error) -> { + if (error != null) { + result.completeExceptionally(error); + return; + } + + if(buffer.hasRemaining()) { + readFully(buffer, result); + return; + } + + buffer.flip(); + result.complete(buffer); + }); + } + + private static final class Plain extends SocketTransport { + private Plain(AsynchronousSocketChannel channel) { + super(channel); + } + + @Override + boolean isSecure() { + return false; + } + + @Override + > R read(ByteBuffer buffer, boolean lastRead, R result) { + return readPlain(buffer, lastRead, result); + } + + @Override + > R write(ByteBuffer buffer, R result) { + return writePlain(buffer, result); + } + + @Override + CompletableFuture handshake() { + return CompletableFuture.completedFuture(null); + } + } + + private static final class Secure extends SocketTransport { + private final AtomicBoolean sslHandshakeCompleted; + private final Object sslHandshakeLock; + private final SSLEngine sslEngine; + private final ByteBuffer sslReadBuffer; + private final ByteBuffer sslWriteBuffer; + private final ByteBuffer sslOutputBuffer; + private Response.Future sslHandshake; + private Secure(AsynchronousSocketChannel channel, SSLEngine sslEngine) { + super(channel); + this.sslHandshakeCompleted = new AtomicBoolean(); + this.sslHandshakeLock = new Object(); + sslHandshakeCompleted.set(sslEngine == null); + this.sslEngine = sslEngine; + var bufferSize = sslEngine.getSession().getPacketBufferSize(); + this.sslReadBuffer = ByteBuffer.allocate(bufferSize); + this.sslWriteBuffer = ByteBuffer.allocate(bufferSize); + this.sslOutputBuffer = ByteBuffer.allocate(bufferSize); + } + + @Override + boolean isSecure() { + return true; + } + + @Override + CompletableFuture handshake() { + try { + if(sslEngine == null) { + return CompletableFuture.completedFuture(null); + } + + if(sslHandshakeCompleted.get()) { + return CompletableFuture.completedFuture(null); + } + + if(sslHandshake != null) { + return sslHandshake; + } + + synchronized (sslHandshakeLock) { + if(sslHandshake != null) { + return sslHandshake; + } + + this.sslHandshake = new Response.Future<>(); + sslEngine.beginHandshake(); + sslReadBuffer.position(sslReadBuffer.limit()); + handleSslHandshakeStatus(null); + return sslHandshake; + } + } catch (Throwable throwable) { + return CompletableFuture.failedFuture(throwable); + } + } + + private void handleSslHandshakeStatus(Status status){ + switch (sslEngine.getHandshakeStatus()) { + case NEED_WRAP -> doSslHandshakeWrap(); + case NEED_UNWRAP, NEED_UNWRAP_AGAIN -> doSslHandshakeUnwrap(status == Status.BUFFER_UNDERFLOW); + case NEED_TASK -> doSslHandshakeTasks(); + case FINISHED -> finishSslHandshake(); + case NOT_HANDSHAKING -> sslHandshake.completeExceptionally(new IOException("Cannot complete handshake")); + } + } + + private void finishSslHandshake() { + sslHandshakeCompleted.set(true); + sslOutputBuffer.clear(); + sslHandshake.complete(null); + } + + private void doSslHandshakeTasks() { + Runnable runnable; + while ((runnable = sslEngine.getDelegatedTask()) != null) { + runnable.run(); + } + + handleSslHandshakeStatus(null); + } + + private void doSslHandshakeUnwrap(boolean forceRead) { + sslReadBuffer.compact(); + if (!forceRead && sslReadBuffer.position() != 0) { + sslReadBuffer.flip(); + doSSlHandshakeUnwrapOperation(); + return; + } + + readPlain(sslReadBuffer, true, (Response.Callback) (ignored, error) -> { + if(error != null) { + sslHandshake.completeExceptionally(error); + return; + } + + doSSlHandshakeUnwrapOperation(); + }); + } + + private void doSSlHandshakeUnwrapOperation() { + try { + var result = sslEngine.unwrap(sslReadBuffer, sslOutputBuffer); + if(isHandshakeFinished(result, false)) { + finishSslHandshake(); + }else { + handleSslHandshakeStatus(result.getStatus()); + } + }catch(Throwable throwable) { + sslHandshake.completeExceptionally(throwable); + } + } + + private void doSslHandshakeWrap() { + try { + sslWriteBuffer.clear(); + var result = sslEngine.wrap(sslOutputBuffer, sslWriteBuffer); + var isHandshakeFinished = isHandshakeFinished(result, true); + sslWriteBuffer.flip(); + writePlain(sslWriteBuffer, (Response.Callback) (ignored, error) -> { + if(error != null) { + sslHandshake.completeExceptionally(error); + return; + } + + if(isHandshakeFinished) { + finishSslHandshake(); + }else { + handleSslHandshakeStatus(null); + } + }); + }catch (Throwable throwable) { + sslHandshake.completeExceptionally(throwable); + } + } + + private boolean isHandshakeFinished(SSLEngineResult result, boolean wrap) { + var sslEngineStatus = result.getStatus(); + if (sslEngineStatus != Status.OK && (wrap || sslEngineStatus != Status.BUFFER_UNDERFLOW)) { + throw new IllegalStateException("SSL handshake operation failed with status: " + sslEngineStatus); + } + + if (wrap && result.bytesConsumed() != 0) { + throw new IllegalStateException("SSL handshake operation failed with status: no bytes consumed"); + } + + if (!wrap && result.bytesProduced() != 0) { + throw new IllegalStateException("SSL handshake operation failed with status: no bytes produced"); + } + + var sslHandshakeStatus = result.getHandshakeStatus(); + return sslHandshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED; + } + + @Override + > R read(ByteBuffer buffer, boolean lastRead, R result) { + try { + if(!sslHandshakeCompleted.get()) { + return readPlain(buffer, lastRead, result); + } + + var bytesCopied = readFromBufferedOutput(buffer, lastRead); + if(bytesCopied != 0) { + result.complete(bytesCopied); + }else if (sslReadBuffer.hasRemaining()) { + decodeSslBuffer(buffer, lastRead, result); + }else { + fillSslBuffer(buffer, lastRead, result); + } + + return result; + }catch (Throwable throwable) { + result.completeExceptionally(throwable); + return result; + } + } + + private > void fillSslBuffer(ByteBuffer buffer, boolean lastRead, R result) { + sslReadBuffer.compact(); + readPlain(sslReadBuffer, true, (Response.Callback) (ignored, error) -> { + if (error != null) { + result.completeExceptionally(error); + return; + } + + decodeSslBuffer(buffer, lastRead, result); + }); + } + + private void decodeSslBuffer(ByteBuffer buffer, boolean lastRead, Response result) { + try { + var unwrapResult = sslEngine.unwrap(sslReadBuffer, sslOutputBuffer); + switch (unwrapResult.getStatus()) { + case OK -> { + if (unwrapResult.bytesProduced() == 0) { + sslOutputBuffer.mark(); + read(buffer, lastRead , result); + } else { + var bytesCopied = readFromBufferedOutput(buffer, lastRead); + result.complete(bytesCopied); + } + } + case BUFFER_UNDERFLOW -> fillSslBuffer(buffer, lastRead, result); + case BUFFER_OVERFLOW -> result.completeExceptionally(new IllegalStateException("SSL output buffer overflow")); + case CLOSED -> result.completeExceptionally(new EOFException()); + } + }catch (Throwable throwable) { + result.completeExceptionally(throwable); + } + } + + private int readFromBufferedOutput(ByteBuffer buffer, boolean lastRead) { + var writePosition = sslOutputBuffer.position(); + if(writePosition == 0) { + return 0; + } + + var bytesRead = 0; + var writeLimit = sslOutputBuffer.limit(); + sslOutputBuffer.limit(writePosition); + try { + sslOutputBuffer.reset(); // Go back to last read position + }catch (InvalidMarkException exception) { + sslOutputBuffer.flip(); // This can happen if unwrapResult.bytesProduced() != 0 on the first call + } + while (buffer.hasRemaining() && sslOutputBuffer.hasRemaining()) { + buffer.put(sslOutputBuffer.get()); + bytesRead++; + } + + if(!sslOutputBuffer.hasRemaining()) { + sslOutputBuffer.clear(); + sslOutputBuffer.mark(); + }else { + sslOutputBuffer.limit(writeLimit); + sslOutputBuffer.mark(); + sslOutputBuffer.position(writePosition); + } + + if(lastRead) { + buffer.flip(); + } + + return bytesRead; + } + + @Override + > R write(ByteBuffer buffer, R result) { + if(!sslHandshakeCompleted.get()) { + return writePlain(buffer, result); + } + + writeSecure(buffer, result); + return result; + } + + private > void writeSecure(ByteBuffer buffer, R result) { + if(!buffer.hasRemaining()) { + result.complete(null); + return; + } + + try { + sslWriteBuffer.clear(); + var wrapResult = sslEngine.wrap(buffer, sslWriteBuffer); + var status = wrapResult.getStatus(); + if (status != Status.OK && status != Status.BUFFER_OVERFLOW) { + throw new IllegalStateException("SSL wrap failed with status: " + status); + } + + sslWriteBuffer.flip(); + writePlain(sslWriteBuffer, (Response.Callback) (ignored, error) -> { + if(error != null) { + result.completeExceptionally(error); + return; + } + + writeSecure(buffer, result); + }); + }catch (SSLException exception) { + result.completeExceptionally(exception); + } + } + } + } + + private sealed static abstract class SocketConnection { + // Necessary because of a bug in the JDK + // The number 50 was reached after debugging to find the best possible number to minimize connection time + // The problem with this approach is that 50 threads ask for a bad connection, they block other instances + // Shouldn't be a problem because the connection would error out immediately, but it should be looked into + // Also maybe find a fix, so I can report it to Oracle + private static final Semaphore CONNECTION_SEMAPHORE = new Semaphore(50, true); + final AsynchronousSocketChannel channel; + final SocketTransport socketTransport; + final URI proxy; + private SocketConnection(AsynchronousSocketChannel channel, SocketTransport socketTransport, URI proxy) { + this.channel = channel; + this.socketTransport = socketTransport; + this.proxy = proxy; + } + + private static SocketConnection of(AsynchronousSocketChannel channel, SocketTransport socketTransport, URI proxy) { + return switch (Proxies.toProxy(proxy).type()) { + case DIRECT -> new NoProxy(channel); + case HTTP -> new HttpProxy(channel, socketTransport, proxy); + case SOCKS -> new SocksProxy(channel, socketTransport, proxy); + }; + } + + CompletableFuture connectAsync(InetSocketAddress address, int timeout) { + return CompletableFuture.runAsync(() -> connectSync(address), Thread::startVirtualThread) + .orTimeout(timeout > 0 ? timeout : DEFAULT_CONNECTION_TIMEOUT, TimeUnit.SECONDS); + } + + private void connectSync(InetSocketAddress address) { + try { + CONNECTION_SEMAPHORE.acquire(); + var start = System.currentTimeMillis(); + var future = channel.connect(address); + future.get(); + }catch (Throwable throwable) { + throw new RuntimeException("Cannot connect to " + address, throwable); + }finally { + CONNECTION_SEMAPHORE.release(); + } + } + + private static final class NoProxy extends SocketConnection { + private NoProxy(AsynchronousSocketChannel channel) { + super(channel, null, null); + } + + @Override + public CompletableFuture connectAsync(InetSocketAddress address, int timeout) { + return super.connectAsync(address, timeout); + } + } + + private static final class HttpProxy extends SocketConnection { + private static final int DEFAULT_RCV_BUF = 8192; + private static final int OK_STATUS_CODE = 200; + + private HttpProxy(AsynchronousSocketChannel channel, SocketTransport socketTransport, URI proxy) { + super(channel, socketTransport, proxy); + } + + @Override + public CompletableFuture connectAsync(InetSocketAddress address, int timeout) { + return super.connectAsync(new InetSocketAddress(proxy.getHost(), proxy.getPort()), timeout) + .thenComposeAsync(openResult -> sendAuthentication(address)) + .thenComposeAsync(connectionResult -> readAuthenticationResponse()) + .thenComposeAsync(this::handleAuthentication); + } + + private CompletableFuture handleAuthentication(String response) { + var responseParts = response.split(" "); + if(responseParts.length < 2) { + return CompletableFuture.failedFuture(new SocketException("HTTP : Cannot connect to proxy, malformed response: " + response)); + } + + var statusCodePart = responseParts[1]; + try { + var statusCode = statusCodePart == null ? -1 : Integer.parseUnsignedInt(statusCodePart); + if(statusCode != OK_STATUS_CODE) { + return CompletableFuture.failedFuture(new SocketException("HTTP : Cannot connect to proxy, status code " + statusCode)); + } + + return CompletableFuture.completedFuture(null); + }catch (Throwable throwable) { + return CompletableFuture.failedFuture(new SocketException("HTTP : Cannot connect to proxy: " + response)); + } + } + + private CompletableFuture readAuthenticationResponse() { + var future = new CompletableFuture(); + var buffer = ByteBuffer.allocate(readReceiveBufferSize()); + socketTransport.read(buffer, true, (Response.Callback) (result, error) -> { + if (error != null) { + future.completeExceptionally(new SocketException("HTTP : Cannot read authentication response", error)); + return; + } + + var data = new byte[result]; + buffer.get(data); + future.complete(new String(data)); + }); + return future; + } + + private int readReceiveBufferSize() { + try { + return channel.getOption(StandardSocketOptions.SO_RCVBUF); + }catch (IOException exception) { + return DEFAULT_RCV_BUF; + } + } + + private CompletableFuture sendAuthentication(InetSocketAddress endpoint) { + var builder = new StringBuilder(); + builder.append("CONNECT ") + .append(endpoint.getHostName()) + .append(":") + .append(endpoint.getPort()) + .append(" HTTP/1.1\r\n"); + builder.append("Host: ") + .append(endpoint.getHostName()) + .append(":") + .append(endpoint.getPort()) + .append("\r\n"); + var authInfo = proxy.getUserInfo(); + if (authInfo != null) { + builder.append("Proxy-Authorization: Basic ") + .append(Base64.getEncoder().encodeToString(authInfo.getBytes())) + .append("\r\n"); + } + builder.append("\r\n"); + var result = new Response.Future(); + socketTransport.write(ByteBuffer.wrap(builder.toString().getBytes()), result); + return result; + } + } + + private static final class SocksProxy extends SocketConnection { + private static final byte VERSION_5 = 5; + + private static final int NO_AUTH = 0; + private static final int USER_PASSW = 2; + private static final int NO_METHODS = -1; + + private static final int CONNECT = 1; + + private static final int IPV4 = 1; + private static final int DOMAIN_NAME = 3; + private static final int IPV6 = 4; + + private static final int REQUEST_OK = 0; + private static final int GENERAL_FAILURE = 1; + private static final int NOT_ALLOWED = 2; + private static final int NET_UNREACHABLE = 3; + private static final int HOST_UNREACHABLE = 4; + private static final int CONN_REFUSED = 5; + private static final int TTL_EXPIRED = 6; + private static final int CMD_NOT_SUPPORTED = 7; + private static final int ADDR_TYPE_NOT_SUP = 8; + + private SocksProxy(AsynchronousSocketChannel channel, SocketTransport socketTransport, URI proxy) { + super(channel, socketTransport, proxy); + } + + + @Override + public CompletableFuture connectAsync(InetSocketAddress address, int timeout) { + return super.connectAsync(new InetSocketAddress(proxy.getHost(), proxy.getPort()), timeout) + .thenComposeAsync(openResult -> sendAuthenticationRequest()) + .thenComposeAsync(response -> sendAuthenticationData(address, response)); + } + + private CompletableFuture sendAuthenticationRequest() { + var connectionPayload = new ByteArrayOutputStream(); + connectionPayload.write(VERSION_5); + connectionPayload.write(2); + connectionPayload.write(NO_AUTH); + connectionPayload.write(USER_PASSW); + var result = new Response.Future(); + socketTransport.write(ByteBuffer.wrap(connectionPayload.toByteArray()), result); + return result.thenComposeAsync(connectionResult -> readServerResponse(2, "Cannot read authentication request response")); + } + + private CompletionStage sendAuthenticationData(InetSocketAddress address, ByteBuffer response) { + var socksVersion = response.get(); + if (socksVersion != VERSION_5) { + return CompletableFuture.failedFuture(new SocketException("SOCKS : Invalid version")); + } + + var method = response.get(); + if (method == NO_METHODS) { + return CompletableFuture.failedFuture(new SocketException("SOCKS : No acceptable methods")); + } + + if (method == NO_AUTH) { + return sendConnectionData(address, null); + } + + if (method != USER_PASSW) { + return CompletableFuture.failedFuture(new SocketException("SOCKS : authentication failed")); + } + + var userInfo = Proxies.parseUserInfo(proxy.getUserInfo()); + if (userInfo == null) { + return CompletableFuture.failedFuture(new SocketException("SOCKS : missing user info")); + } + + var outputStream = new ByteArrayOutputStream(); + outputStream.write(1); + outputStream.write(userInfo.username().length()); + outputStream.writeBytes(userInfo.username().getBytes(StandardCharsets.ISO_8859_1)); + if (userInfo.password() != null) { + outputStream.write(userInfo.password().length()); + outputStream.writeBytes(userInfo.password().getBytes(StandardCharsets.ISO_8859_1)); + } else { + outputStream.write(0); + } + var result = new Response.Future(); + socketTransport.write(ByteBuffer.wrap(outputStream.toByteArray()), result); + return result.thenComposeAsync(connectionResult -> readServerResponse(2, "Cannot read authentication data response")) + .thenComposeAsync(connectionResponse -> sendConnectionData(address, connectionResponse)); + } + + private CompletableFuture sendConnectionData(InetSocketAddress address, ByteBuffer connectionResponse) { + if(connectionResponse != null && connectionResponse.get(1) != 0) { + return CompletableFuture.failedFuture(new SocketException("SOCKS : authentication failed")); + } + + var outputStream = new ByteArrayOutputStream(); + outputStream.write(VERSION_5); + outputStream.write(CONNECT); + outputStream.write(0); + outputStream.write(DOMAIN_NAME); + outputStream.write(address.getHostName().length()); + outputStream.writeBytes(address.getHostName().getBytes(StandardCharsets.ISO_8859_1)); + outputStream.write((address.getPort() >> 8) & 0xff); + outputStream.write((address.getPort()) & 0xff); + var result = new Response.Future(); + socketTransport.write(ByteBuffer.wrap(outputStream.toByteArray()), result); + return result.thenComposeAsync(authenticationResult -> readServerResponse(4, "Cannot read connection data response")) + .thenComposeAsync(this::onConnected); + } + + private CompletableFuture onConnected(ByteBuffer authenticationResponse) { + if(authenticationResponse.limit() < 2) { + return CompletableFuture.failedFuture(new SocketException("SOCKS malformed response")); + } + + return switch (authenticationResponse.get(1)) { + case REQUEST_OK -> onConnected(authenticationResponse.get(3)); + case GENERAL_FAILURE -> CompletableFuture.failedFuture(new SocketException("SOCKS server general failure")); + case NOT_ALLOWED -> CompletableFuture.failedFuture(new SocketException("SOCKS: Connection not allowed by ruleset")); + case NET_UNREACHABLE -> CompletableFuture.failedFuture(new SocketException("SOCKS: Network unreachable")); + case HOST_UNREACHABLE -> CompletableFuture.failedFuture(new SocketException("SOCKS: Host unreachable")); + case CONN_REFUSED -> CompletableFuture.failedFuture(new SocketException("SOCKS: Connection refused")); + case TTL_EXPIRED -> CompletableFuture.failedFuture(new SocketException("SOCKS: TTL expired")); + case CMD_NOT_SUPPORTED -> CompletableFuture.failedFuture(new SocketException("SOCKS: Command not supported")); + case ADDR_TYPE_NOT_SUP -> CompletableFuture.failedFuture(new SocketException("SOCKS: address type not supported")); + default -> CompletableFuture.failedFuture(new SocketException("SOCKS: unhandled error")); + }; + } + + private CompletableFuture onConnected(byte authenticationType) { + return switch (authenticationType) { + case IPV4 -> readServerResponse(4, "Cannot read IPV4 address") + .thenComposeAsync(ipResult -> readServerResponse(2, "Cannot read IPV4 port")) + .thenRun(() -> {}); + case IPV6 -> readServerResponse(16, "Cannot read IPV6 address") + .thenComposeAsync(ipResult -> readServerResponse(2, "Cannot read IPV6 port")) + .thenRun(() -> {}); + case DOMAIN_NAME -> readServerResponse(1, "Cannot read domain name") + .thenComposeAsync(domainLengthBuffer -> readServerResponse(Byte.toUnsignedInt(domainLengthBuffer.get()), "Cannot read domain hostname")) + .thenComposeAsync(ipResult -> readServerResponse(2, "Cannot read domain port")) + .thenRun(() -> {}); + default -> CompletableFuture.failedFuture(new SocketException("Reply from SOCKS server contains wrong code")); + }; + } + + private CompletableFuture readServerResponse(int length, String errorMessage) { + var buffer = ByteBuffer.allocate(length); + var result = new Response.Future(); + socketTransport.readFully(buffer, result); + return result.exceptionallyCompose(error -> CompletableFuture.failedFuture(new SocketException(errorMessage, error))); + } + } + } +} diff --git a/src/main/java/it/auties/whatsapp/net/WebSocketClient.java b/src/main/java/it/auties/whatsapp/net/WebSocketClient.java new file mode 100644 index 000000000..f32488baf --- /dev/null +++ b/src/main/java/it/auties/whatsapp/net/WebSocketClient.java @@ -0,0 +1,744 @@ +package it.auties.whatsapp.net; + +import it.auties.whatsapp.crypto.Sha1; +import it.auties.whatsapp.util.Bytes; + +import javax.net.ssl.SSLEngine; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketException; +import java.net.URI; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CodingErrorAction; +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; +import java.util.Base64; +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import static java.lang.String.format; +import static java.nio.charset.StandardCharsets.UTF_8; + +// The message encoding/decoding is a minimal copy from the OpenJDK source +// No text IO / no partial message output +// IO is not synchronized as it's assumed that SocketHandler will do that +public class WebSocketClient implements AutoCloseable { + private static final int DEFAULT_CONNECTION_TIMEOUT = 300; + private static final String DEFAULT_PATH = "/"; + private static final int KEY_LENGTH = 16; + private static final int VERSION = 13; + private static final int SWITCHING_PROTOCOLS_CODE = 101; + private static final String SERVER_KEY_HEADER = "sec-websocket-accept"; + private static final int MAX_HEADER_SIZE_BYTES = 2 + 8 + 4; + private static final int MAX_CONTROL_FRAME_PAYLOAD_LENGTH = 125; + private static final int NORMAL_CLOSURE = 1000; + private static final int NO_STATUS_CODE = 1005; + private static final int CLOSED_ABNORMALLY = 1006; + private static final String MAGIC_VALUE = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + + private final SocketClient underlyingSocket; + private final String clientKey; + private final MessageDecoder messageDecoder; + private final MessageEncoder messageEncoder; + private final Listener listener; + private String serverKey; + + private WebSocketClient(SocketClient underlyingSocket, Listener listener) { + this.underlyingSocket = underlyingSocket; + this.clientKey = Base64.getEncoder().encodeToString(Bytes.random(KEY_LENGTH)); + this.messageDecoder = new MessageDecoder(); + this.messageEncoder = new MessageEncoder(underlyingSocket.getSendBufferSize()); + this.listener = listener; + } + + public static WebSocketClient newPlainClient(URI proxy, Listener listener) throws IOException { + var underlyingSocket = SocketClient.newPlainClient(proxy); + return new WebSocketClient(underlyingSocket, listener); + } + + + public static WebSocketClient newSecureClient(SSLEngine sslEngine, URI proxy, Listener listener) throws IOException { + var underlyingSocket = SocketClient.newSecureClient(sslEngine, proxy); + return new WebSocketClient(underlyingSocket, listener); + } + + public CompletableFuture connectAsync(InetSocketAddress address, String path) { + return connectAsync(address, path, DEFAULT_CONNECTION_TIMEOUT); + } + + public CompletableFuture connectAsync(InetSocketAddress address, String path, int timeout) { + return underlyingSocket.connectAsync(address, timeout) + .thenComposeAsync(ignored -> handshake(path)) + .thenRunAsync(this::listen) + .orTimeout(timeout, TimeUnit.SECONDS); + } + + private void listen() { + var buffer = ByteBuffer.allocate(underlyingSocket.getReceiveBufferSize()); + listen(buffer, listener); + } + + private void listen(ByteBuffer buffer, Listener listener) { + buffer.clear(); + underlyingSocket.readAsync(buffer, (bytesRead, error) -> { + if (error != null) { + close(); + return; + } + + messageDecoder.readFrame(buffer, listener); + listen(buffer, listener); + }); + } + + + private CompletableFuture handshake(String path) { + var payload = generateWebSocketUpgradePayload(path); + return underlyingSocket.writeAsync(ByteBuffer.wrap(payload.getBytes())) + .thenComposeAsync(writeResult -> readWebSocketUpgradeResponse()) + .thenComposeAsync(this::parseWebSocketUpgradeResponse); + } + + private CompletableFuture parseWebSocketUpgradeResponse(String result) { + if (result.isEmpty()) { + return CompletableFuture.failedFuture(new IllegalArgumentException("Malformed HTTP response")); + } + + var lines = result.split("\r\n"); + var responseParts = lines[0].split(" ", 3); + try { + var statusCode = Integer.parseUnsignedInt(responseParts[1]); + if (statusCode != SWITCHING_PROTOCOLS_CODE) { + return CompletableFuture.failedFuture(new SocketException("Cannot upgrade protocol to WebSocket, status code " + statusCode)); + } + + for (var i = 1; i < lines.length; i++) { + var header = lines[i].split(": ", 2); + if (header.length != 2) { + continue; + } + + var headerKey = header[0]; + if (!headerKey.equalsIgnoreCase(SERVER_KEY_HEADER)) { + continue; + } + + serverKey = header[1]; + break; + } + + if (serverKey == null) { + return CompletableFuture.failedFuture(new SocketException("Missing Sec-WebSocket-Accept header")); + } + + var hash = Sha1.calculate(Bytes.concat(clientKey.getBytes(), MAGIC_VALUE.getBytes())); + if (!Objects.equals(serverKey, Base64.getEncoder().encodeToString(hash))) { + return CompletableFuture.failedFuture(new SocketException("Invalid Sec-WebSocket-Accept header")); + } + + return CompletableFuture.completedFuture(null); + } catch (Throwable throwable) { + return CompletableFuture.failedFuture(new SocketException("Malformed HTTP response: " + responseParts[0])); + } + } + + private String generateWebSocketUpgradePayload(String path) { + var socketAddress = getSocketAddress(); + var urlHost = socketAddress.getHostName(); + var urlPort = socketAddress.getPort(); + var urlPath = Objects.requireNonNullElse(path, DEFAULT_PATH); + return """ + GET %s HTTP/1.1\r + Host: %s:%d\r + Connection: Upgrade\r + Upgrade: websocket\r + Sec-WebSocket-Version: %d\r + Sec-WebSocket-Key: %s\r + \r + """.formatted(urlPath, urlHost, urlPort, VERSION, clientKey); + } + + private CompletableFuture readWebSocketUpgradeResponse() { + var buffer = ByteBuffer.allocate(underlyingSocket.getReceiveBufferSize()); + return underlyingSocket.readAsync(buffer).thenApplyAsync(bytesRead -> { + var data = new byte[buffer.limit()]; + buffer.get(data); + return new String(data); + }); + } + + private InetSocketAddress getSocketAddress() { + var address = underlyingSocket.getRemoteSocketAddress(); + if (!(address instanceof InetSocketAddress inetSocketAddress)) { + throw new IllegalStateException("Cannot query socket address: unsupported address type"); + } + + return inetSocketAddress; + } + + public CompletableFuture sendBinary(ByteBuffer buffer) { + try { + var dst = messageEncoder.encodeBinary(buffer, true); + return underlyingSocket.writeAsync(dst); + } catch (IOException exception) { + return CompletableFuture.failedFuture(exception); + } + } + + @Override + public void close() { + if(underlyingSocket.isClosed()) { + return; + } + + closeWebSocket(); + closeUnderlyingSocket(); + listener.onClose(NORMAL_CLOSURE, ""); + } + + private void closeWebSocket() { + try { + var dst = messageEncoder.encodeClose(NORMAL_CLOSURE, CharBuffer.allocate(0)); + var future = underlyingSocket.writeAsync(dst); + future.join(); + }catch(Throwable ignored) { + + } + } + + private void closeUnderlyingSocket() { + try { + underlyingSocket.close(); + }catch (Throwable ignored1) { + + } + } + + public interface Listener { + default void onMessage(ByteBuffer data, boolean last) { + + } + + default void onClose(int statusCode, String reason) { + + } + } + + private static final class MessageDecoder { + private static final int AWAITING_FIRST_BYTE = 1; + private static final int AWAITING_SECOND_BYTE = 2; + private static final int READING_16_LENGTH = 4; + private static final int READING_64_LENGTH = 8; + private static final int READING_PAYLOAD = 32; + + private final ByteBuffer accumulator; + private boolean fin; + private MessageOpcode opcode; + private MessageOpcode originatingOpcode; + private long payloadLen; + private long unconsumedPayloadLen; + private ByteBuffer binaryData; + private int state; + private long remainingPayloadLength; + + public MessageDecoder() { + this.accumulator = ByteBuffer.allocate(8); + this.state = AWAITING_FIRST_BYTE; + } + + public void readFrame(ByteBuffer input, Listener listener) { + while (true) { + var result = handleFrame(input, listener); + if (result) { + break; + } + } + } + + private boolean handleFrame(ByteBuffer input, Listener listener) { + return switch (state) { + case AWAITING_FIRST_BYTE -> handleOpcode(input); + case AWAITING_SECOND_BYTE -> handlePayloadByte(input); + case READING_16_LENGTH -> handlePayloadLength(input); + case READING_64_LENGTH -> handlePayloadLengthExtended(input); + case READING_PAYLOAD -> handlePayloadData(input, listener); + default -> throw new InternalError(String.valueOf(state)); + }; + } + + private boolean handlePayloadData(ByteBuffer input, Listener listener) { + if (!input.hasRemaining()) { + return true; + } + + var deliverable = (int) Math.min(remainingPayloadLength, input.remaining()); + var oldLimit = input.limit(); + input.limit(input.position() + deliverable); + if (deliverable != 0 || remainingPayloadLength == 0) { + payloadData(input, listener); + } + var consumed = deliverable - input.remaining(); + if (consumed < 0) { + throw new InternalError(); + } + input.limit(oldLimit); + remainingPayloadLength -= consumed; + if (remainingPayloadLength == 0) { + endFrame(listener); + state = AWAITING_FIRST_BYTE; + } + return false; + } + + private boolean handlePayloadLengthExtended(ByteBuffer input) { + if (!input.hasRemaining()) { + return true; + } + + var b = input.get(); + if (accumulator.put(b).position() < 8) { + return false; + } + + remainingPayloadLength = accumulator.flip().getLong(); + if (remainingPayloadLength < 0) { + throw new IllegalArgumentException("Negative payload length: " + remainingPayloadLength); + } else if (remainingPayloadLength < 65536) { + throw new IllegalArgumentException("Not minimally-encoded payload length:" + remainingPayloadLength); + } + payloadLen(remainingPayloadLength); + accumulator.clear(); + state = READING_PAYLOAD; + return false; + } + + private boolean handlePayloadLength(ByteBuffer input) { + if (!input.hasRemaining()) { + return true; + } + + var b = input.get(); + if (accumulator.put(b).position() < 2) { + return false; + } + + remainingPayloadLength = accumulator.flip().getChar(); + if (remainingPayloadLength < 126) { + throw new IllegalArgumentException("Not minimally-encoded payload length:" + remainingPayloadLength); + } + + payloadLen(remainingPayloadLength); + accumulator.clear(); + state = READING_PAYLOAD; + return false; + } + + private boolean handleOpcode(ByteBuffer input) { + if (!input.hasRemaining()) { + return true; + } + + var b = input.get(); + fin = (b & 0b10000000) != 0; + if ((b & 0b01000000) != 0) { + throw new IllegalArgumentException("Unexpected rsv1 bit"); + } + + if ((b & 0b00100000) != 0) { + throw new IllegalArgumentException("Unexpected rsv2 bit"); + } + + if ((b & 0b00010000) != 0) { + throw new IllegalArgumentException("Unexpected rsv3 bit"); + } + + var v = MessageOpcode.ofCode(b); + switch (v) { + case PING, PONG, CLOSE -> { + if (!fin) { + throw new IllegalArgumentException("Fragmented control frame " + v); + } + opcode = v; + } + case TEXT, BINARY -> { + if (originatingOpcode != null) { + throw new IllegalArgumentException(format("Unexpected frame %s (fin=%s)", v, fin)); + } + opcode = v; + if (!fin) { + originatingOpcode = v; + } + } + case CONTINUATION -> { + if (originatingOpcode == null) { + throw new IllegalArgumentException(format("Unexpected frame %s (fin=%s)", v, fin)); + } + + opcode = v; + } + case null, default -> throw new IllegalArgumentException("Unexpected opcode " + v); + } + + state = AWAITING_SECOND_BYTE; + return false; + } + + private boolean handlePayloadByte(ByteBuffer input) { + if (!input.hasRemaining()) { + return true; + } + + var b = input.get(); + if ((b & 0b10000000) != 0) { + throw new IllegalArgumentException("Masked frame received"); + } + + var p1 = (byte) (b & 0b01111111); + if (p1 < 126) { + payloadLen(remainingPayloadLength = p1); + state = READING_PAYLOAD; + } else if (p1 < 127) { + state = READING_16_LENGTH; + } else { + state = READING_64_LENGTH; + } + return false; + } + + private void payloadLen(long value) { + if (opcode.isControl()) { + if (value > MAX_CONTROL_FRAME_PAYLOAD_LENGTH) { + throw new IllegalArgumentException( + format("%s's payload length %s", opcode, value)); + } + assert MessageOpcode.CLOSE.isControl(); + if (opcode == MessageOpcode.CLOSE && value == 1) { + throw new IllegalArgumentException("Incomplete status code"); + } + } + payloadLen = value; + unconsumedPayloadLen = value; + } + + private void payloadData(ByteBuffer data, Listener listener) { + unconsumedPayloadLen -= data.remaining(); + var lastPayloadChunk = unconsumedPayloadLen == 0; + if (opcode.isControl()) { + if (binaryData != null) { + binaryData.put(data); + } else if (!lastPayloadChunk) { + binaryData = ByteBuffer.allocate(MAX_CONTROL_FRAME_PAYLOAD_LENGTH).put(data); + } else { + binaryData = ByteBuffer.allocate(data.remaining()).put(data); + } + } else { + var last = fin && lastPayloadChunk; + var text = opcode == MessageOpcode.TEXT || originatingOpcode == MessageOpcode.TEXT; + if (!text) { + listener.onMessage(data, last); + data.position(data.limit()); + } + } + } + + private void endFrame(Listener listener) { + if (opcode.isControl()) { + binaryData.flip(); + } + switch (opcode) { + case CLOSE -> { + var statusCode = NO_STATUS_CODE; + var reason = ""; + if (payloadLen != 0) { + statusCode = binaryData.getChar(); + if (!isLegalToReceiveFromServer(statusCode)) { + throw new IllegalArgumentException( + "Illegal status code: " + statusCode); + } + try { + reason = UTF_8.newDecoder().decode(binaryData).toString(); + } catch (CharacterCodingException e) { + throw new IllegalArgumentException("Illegal close reason", e); + } + } + listener.onClose(statusCode, reason); + } + case PING, PONG -> binaryData = null; + default -> { + if (fin) { + originatingOpcode = null; + } + } + } + payloadLen = 0; + opcode = null; + } + + private static boolean isLegalToReceiveFromServer(int code) { + if (code < 1000 || code > 65535) { + return false; + } + + if ((code >= 1016 && code <= 2999) || code == 1004) { + return false; + } + + return code != NO_STATUS_CODE && code != CLOSED_ABNORMALLY && code != 1015 && code != 1010; + } + } + + private static final class MessageEncoder { + private final SecureRandom maskingKeySource; + private final CharsetEncoder charsetEncoder; + private final ByteBuffer intermediateBuffer; + private final ByteBuffer outputBuffer; + private final ByteBuffer headerBuffer; + private final ByteBuffer acc; + private final int[] maskBytes; + private int offset; + private long maskLong; + private boolean previousFin = true; + private boolean previousText; + private boolean closed; + private char firstChar; + private long payloadLen; + private int maskingKey; + private boolean mask; + + public MessageEncoder(int maxPayloadSize) { + this.maskingKeySource = new SecureRandom(); + this.charsetEncoder = StandardCharsets.UTF_8.newEncoder() + .onMalformedInput(CodingErrorAction.REPORT) + .onUnmappableCharacter(CodingErrorAction.REPORT); + this.intermediateBuffer = ByteBuffer.allocate(maxPayloadSize); + this.outputBuffer = ByteBuffer.allocate(maxPayloadSize); + this.headerBuffer = ByteBuffer.allocate(MAX_HEADER_SIZE_BYTES); + this.acc = ByteBuffer.allocate(8); + this.maskBytes = new int[4]; + } + + public ByteBuffer encodeBinary(ByteBuffer src, boolean last) throws IOException { + if (closed) { + throw new IOException("Output closed"); + } + + if (previousText && !previousFin) { + throw new IllegalStateException("Unexpected binary message"); + } + + var opcode = previousFin ? MessageOpcode.BINARY : MessageOpcode.CONTINUATION; + setupHeader(opcode, last, src.remaining()); + previousFin = last; + previousText = false; + outputBuffer.clear(); + outputBuffer.put(headerBuffer); + transferMasking(src, outputBuffer); + outputBuffer.flip(); + return outputBuffer; + } + + public ByteBuffer encodeClose(int statusCode, CharBuffer reason) throws IOException { + if (closed) { + throw new IOException("Output closed"); + } + + intermediateBuffer.position(0).limit(MAX_CONTROL_FRAME_PAYLOAD_LENGTH); + intermediateBuffer.putChar((char) statusCode); + var r = charsetEncoder.reset().encode(reason, intermediateBuffer, true); + if (r.isUnderflow()) { + r = charsetEncoder.flush(intermediateBuffer); + } + + if (r.isError()) { + try { + r.throwException(); + } catch (CharacterCodingException e) { + throw new IOException("Malformed reason", e); + } + } else if (r.isOverflow()) { + throw new IOException("Long reason"); + } else if (!r.isUnderflow()) { + throw new InternalError(); + } + + intermediateBuffer.flip(); + setupHeader(MessageOpcode.CLOSE, true, intermediateBuffer.remaining()); + closed = true; + outputBuffer.clear(); + outputBuffer.put(headerBuffer); + transferMasking(intermediateBuffer, outputBuffer); + outputBuffer.flip(); + return outputBuffer; + } + + private void setupHeader(MessageOpcode opcode, boolean fin, long payloadLen) { + headerBuffer.clear(); + var mask = maskingKeySource.nextInt(); + if (mask == 0) { + fin(fin).opcode(opcode).payloadLen(payloadLen).write(headerBuffer); + } else { + fin(fin).opcode(opcode).payloadLen(payloadLen).mask(mask).write(headerBuffer); + } + + headerBuffer.flip(); + acc.clear().putInt(mask).putInt(mask).flip(); + for (int i = 0; i < maskBytes.length; i++) { + maskBytes[i] = acc.get(i); + } + offset = 0; + maskLong = acc.getLong(0); + } + + private void transferMasking(ByteBuffer src, ByteBuffer dst) { + begin(src, dst); + loop(src, dst); + end(src, dst); + } + + private void begin(ByteBuffer src, ByteBuffer dst) { + if (offset == 0) { + return; + } + var i = src.position(); + var j = dst.position(); + var srcLim = src.limit(); + var dstLim = dst.limit(); + for (; offset < 4 && i < srcLim && j < dstLim; i++, j++, offset++) { + dst.put(j, (byte) (src.get(i) ^ maskBytes[offset])); + } + offset &= 3; + src.position(i); + dst.position(j); + } + + private void loop(ByteBuffer src, ByteBuffer dst) { + var i = src.position(); + var j = dst.position(); + var srcLongLim = src.limit() - 7; + var dstLongLim = dst.limit() - 7; + for (; i < srcLongLim && j < dstLongLim; i += 8, j += 8) { + dst.putLong(j, src.getLong(i) ^ maskLong); + } + if (i > src.limit()) { + src.position(i - 8); + } else { + src.position(i); + } + if (j > dst.limit()) { + dst.position(j - 8); + } else { + dst.position(j); + } + } + + private void end(ByteBuffer src, ByteBuffer dst) { + var srcLim = src.limit(); + var dstLim = dst.limit(); + var i = src.position(); + var j = dst.position(); + for (; i < srcLim && j < dstLim; i++, j++, offset = (offset + 1) & 3) { + dst.put(j, (byte) (src.get(i) ^ maskBytes[offset])); + } + src.position(i); + dst.position(j); + } + + private MessageEncoder fin(boolean value) { + if (value) { + firstChar |= 0b10000000_00000000; + } else { + firstChar &= (char) ~0b10000000_00000000; + } + return this; + } + + private MessageEncoder opcode(MessageOpcode value) { + firstChar = (char) ((firstChar & 0xF0FF) | (value.code << 8)); + return this; + } + + private MessageEncoder payloadLen(long value) { + if (value < 0) { + throw new IllegalArgumentException("Negative: " + value); + } + payloadLen = value; + firstChar &= 0b11111111_10000000; + if (payloadLen < 126) { + firstChar |= (char) payloadLen; + } else if (payloadLen < 65536) { + firstChar |= 126; + } else { + firstChar |= 127; + } + return this; + } + + private MessageEncoder mask(int value) { + firstChar |= 0b00000000_10000000; + maskingKey = value; + mask = true; + return this; + } + + private void write(ByteBuffer buffer) { + buffer.putChar(firstChar); + if (payloadLen >= 126) { + if (payloadLen < 65536) { + buffer.putChar((char) payloadLen); + } else { + buffer.putLong(payloadLen); + } + } + if (mask) { + buffer.putInt(maskingKey); + } + } + } + + private enum MessageOpcode { + CONTINUATION(0x0), + TEXT(0x1), + BINARY(0x2), + NON_CONTROL_0x3(0x3), + NON_CONTROL_0x4(0x4), + NON_CONTROL_0x5(0x5), + NON_CONTROL_0x6(0x6), + NON_CONTROL_0x7(0x7), + CLOSE(0x8), + PING(0x9), + PONG(0xA), + CONTROL_0xB(0xB), + CONTROL_0xC(0xC), + CONTROL_0xD(0xD), + CONTROL_0xE(0xE), + CONTROL_0xF(0xF); + + private static final MessageOpcode[] opcodes; + + static { + var values = values(); + opcodes = new MessageOpcode[values.length]; + for (var value : values) { + opcodes[value.code] = value; + } + } + + private final byte code; + + MessageOpcode(int code) { + this.code = (byte) code; + } + + boolean isControl() { + return (code & 0x8) != 0; + } + + static MessageOpcode ofCode(int code) { + return opcodes[code & 0xF]; + } + } +} diff --git a/src/main/java/it/auties/whatsapp/registration/WhatsappMetadata.java b/src/main/java/it/auties/whatsapp/registration/WhatsappMetadata.java index f07ebb4f4..288297e72 100644 --- a/src/main/java/it/auties/whatsapp/registration/WhatsappMetadata.java +++ b/src/main/java/it/auties/whatsapp/registration/WhatsappMetadata.java @@ -1,6 +1,5 @@ package it.auties.whatsapp.registration; -import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import it.auties.curve25519.Curve25519; import it.auties.whatsapp.controller.Keys; @@ -11,82 +10,46 @@ import it.auties.whatsapp.model.business.BusinessVerifiedNameDetailsSpec; import it.auties.whatsapp.model.signal.auth.UserAgent.PlatformType; import it.auties.whatsapp.model.signal.auth.Version; +import it.auties.whatsapp.net.HttpClient; import it.auties.whatsapp.util.Json; -import it.auties.whatsapp.util.Medias; -import it.auties.whatsapp.util.ProxyAuthenticator; -import it.auties.whatsapp.util.Specification.Whatsapp; import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; import java.security.SecureRandom; -import java.util.*; +import java.util.Base64; +import java.util.HexFormat; import java.util.concurrent.CompletableFuture; -import static java.net.http.HttpResponse.BodyHandlers.ofString; - public final class WhatsappMetadata { - static { - ProxyAuthenticator.allowAll(); - } + private static final Version MOBILE_BUSINESS_IOS_VERSION = Version.of("2.24.16.78"); + private static final Version MOBILE_PERSONAL_IOS_VERSION = Version.of("2.24.17.71"); + private static final URI WEB_UPDATE_URL = URI.create("https://web.whatsapp.com/check-update?version=2.2245.9&platform=web"); + private static final String MOBILE_IOS_STATIC = "0a1mLfGUIBVrMKF1RdvLI5lkRBvof6vn0fD2QRSM"; + private static final String MOBILE_BUSINESS_IOS_STATIC = "USUDuDYDeQhY4RF2fCSp5m3F6kJ1M2J8wS7bbNA2"; private static volatile Version webVersion; - private static volatile Version personalIosVersion; - private static volatile Version businessIosVersion; - - + public static CompletableFuture getVersion(PlatformType platform) { return switch (platform) { case WEB, WINDOWS, MACOS -> getWebVersion(); case IOS -> - getIosVersion(false); + CompletableFuture.completedFuture(MOBILE_PERSONAL_IOS_VERSION); case IOS_BUSINESS -> - getIosVersion(true); + CompletableFuture.completedFuture(MOBILE_BUSINESS_IOS_VERSION); default -> throw new IllegalStateException("Unsupported mobile os: " + platform); }; } - private static CompletableFuture getIosVersion(boolean business) { - if (business && businessIosVersion != null) { - return CompletableFuture.completedFuture(businessIosVersion); - } - - if (!business && personalIosVersion != null) { - return CompletableFuture.completedFuture(personalIosVersion); - } - - return Medias.downloadAsync(URI.create(business ? Whatsapp.MOBILE_BUSINESS_IOS_URL : Whatsapp.MOBILE_IOS_URL), Whatsapp.MOBILE_IOS_USER_AGENT).thenApplyAsync(response -> { - var result = Json.readValue(response, IosVersionResponse.class) - .version() - .filter(version -> String.valueOf(version.tertiary()).length() != 1 || String.valueOf(version.quaternary()).length() != 1) - .orElse(business ? Whatsapp.MOBILE_DEFAULT_BUSINESS_IOS_VERSION : Whatsapp.MOBILE_DEFAULT_PERSONAL_IOS_VERSION); - if(business) { - businessIosVersion = result; - }else { - personalIosVersion = result; - } - return result; - }); - } - private static CompletableFuture getWebVersion() { if (webVersion != null) { return CompletableFuture.completedFuture(webVersion); } - try (var client = HttpClient.newHttpClient()) { - var request = HttpRequest.newBuilder() - .GET() - .uri(URI.create(Whatsapp.WEB_UPDATE_URL)) - .build(); - return client.sendAsync(request, ofString()).thenApplyAsync(response -> { - var webVersionResponse = Json.readValue(response.body(), WebVersionResponse.class); - return webVersion = Version.of(webVersionResponse.currentVersion()); - }); - } catch (Throwable throwable) { - throw new RuntimeException("Cannot fetch latest web version", throwable); - } + var client = new HttpClient(HttpClient.Platform.DEFAULT, false); + return client.getString(WEB_UPDATE_URL).thenApplyAsync(response -> { + var webVersionResponse = Json.readValue(response, WebVersionResponse.class); + return webVersion = Version.of(webVersionResponse.currentVersion()); + }); } public static CompletableFuture getToken(long phoneNumber, PlatformType platform, Version appVersion) { @@ -97,7 +60,7 @@ public static CompletableFuture getToken(long phoneNumber, PlatformType } private static CompletableFuture getIosToken(long phoneNumber, Version version, boolean business) { - var staticToken = business ? Whatsapp.MOBILE_BUSINESS_IOS_STATIC : Whatsapp.MOBILE_IOS_STATIC; + var staticToken = business ? MOBILE_BUSINESS_IOS_STATIC : MOBILE_IOS_STATIC; var token = staticToken + HexFormat.of().formatHex(version.toHash()) + phoneNumber; return CompletableFuture.completedFuture(HexFormat.of().formatHex(MD5.calculate(token))); } @@ -125,32 +88,4 @@ private record WebVersionResponse(@JsonProperty("isBroken") boolean broken, @JsonProperty("currentVersion") String currentVersion) { } - - private static final class IosVersionResponse { - private static final IosVersionResponse EMPTY = new IosVersionResponse(null); - private final Version version; - IosVersionResponse(Version version) { - this.version = version; - } - - @SuppressWarnings("unchecked") - @JsonCreator - public static IosVersionResponse of(Map json) { - var results = (List>) json.get("results"); - if (results.isEmpty()) { - return EMPTY; - } - - var result = (String) results.getFirst().get("version"); - if(result == null) { - return EMPTY; - } - - return new IosVersionResponse(Version.of("2." + result)); - } - - public Optional version() { - return Optional.of(version); - } - } } \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/registration/WhatsappRegistration.java b/src/main/java/it/auties/whatsapp/registration/WhatsappRegistration.java index 97d40b2ec..ba8c6b290 100644 --- a/src/main/java/it/auties/whatsapp/registration/WhatsappRegistration.java +++ b/src/main/java/it/auties/whatsapp/registration/WhatsappRegistration.java @@ -13,14 +13,10 @@ import it.auties.whatsapp.model.response.AbPropsResponse; import it.auties.whatsapp.model.response.RegistrationResponse; import it.auties.whatsapp.model.signal.keypair.SignalKeyPair; +import it.auties.whatsapp.net.HttpClient; import it.auties.whatsapp.util.*; -import it.auties.whatsapp.util.Specification.Whatsapp; -import java.net.*; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.net.http.HttpResponse.BodyHandlers; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.Map.Entry; @@ -29,9 +25,8 @@ import java.util.stream.Collectors; public final class WhatsappRegistration { - static { - Authenticator.setDefault(ProxyAuthenticator.globalAuthenticator()); - } + public static final String MOBILE_REGISTRATION_ENDPOINT = "https://v.whatsapp.net/v2"; + private static final byte[] REGISTRATION_PUBLIC_KEY = HexFormat.of().parseHex("8e8c0f74c3ebc5d7a6865c6c3c843856b06121cce8ea774d22fb6f122512302d"); private final HttpClient httpClient; private final Store store; @@ -44,7 +39,7 @@ public WhatsappRegistration(Store store, Keys keys, AsyncVerificationCodeSupplie this.keys = keys; this.codeHandler = codeHandler; this.method = method; - this.httpClient = createClient(); + this.httpClient = new HttpClient(store.device().platform().isIOS() ? HttpClient.Platform.IOS : HttpClient.Platform.ANDROID); } public CompletableFuture registerPhoneNumber() { @@ -97,22 +92,12 @@ private CompletableFuture onboard(String cc, Long in, String ab .put("rc", store.releaseChannel().index()) .put("ab_hash", abHash, abHash != null) .toMap(); - System.out.println(Whatsapp.MOBILE_REGISTRATION_ENDPOINT + "/reg_onboard_abprop?" + toFormParams(attributes)); - var request = HttpRequest.newBuilder() - .uri(URI.create(Whatsapp.MOBILE_REGISTRATION_ENDPOINT + "/reg_onboard_abprop?" + toFormParams(attributes))) - .GET() - .header("User-Agent", store.device().toUserAgent(store.version())) - .header("Content-Type","application/x-www-form-urlencoded") - .build(); - return httpClient.sendAsync(request, BodyHandlers.ofString()) - .thenApply(response -> { - if (response.statusCode() != HttpURLConnection.HTTP_OK) { - throw new RegistrationException(null, response.body()); - } - - System.out.println(response.body()); - return Json.readValue(response.body(), AbPropsResponse.class); - }); + var headers = Map.of( + "User-Agent", store.device().toUserAgent(store.version()), + "Content-Type","application/x-www-form-urlencoded" + ); + return httpClient.getString(URI.create(MOBILE_REGISTRATION_ENDPOINT + "/reg_onboard_abprop?" + toFormParams(attributes)), headers) + .thenApply(response -> Json.readValue(response, AbPropsResponse.class)); } private CompletableFuture exists(VerificationCodeError lastError) { @@ -124,11 +109,7 @@ private CompletableFuture exists(VerificationCodeError las ios ? Map.entry("recovery_token_error", "-25300") : null ); return options.thenComposeAsync(attrs -> sendRequest("/exist", attrs)).thenComposeAsync(result -> { - if (result.statusCode() != HttpURLConnection.HTTP_OK) { - throw new RegistrationException(null, result.body()); - } - - var response = Json.readValue(result.body(), RegistrationResponse.class); + var response = Json.readValue(result, RegistrationResponse.class); if (response.errorReason() == VerificationCodeError.INCORRECT) { return CompletableFuture.completedFuture(response); } @@ -137,7 +118,7 @@ private CompletableFuture exists(VerificationCodeError las return exists(response.errorReason()); } - throw new RegistrationException(response, result.body()); + throw new RegistrationException(response, result); }); } @@ -157,10 +138,8 @@ private CompletableFuture clientLog(T data, Entry... attr false, attributes ); - return options.thenCompose(attrs -> sendRequest("/client_log", attrs)).thenApply(result -> { - System.out.println(result.body()); - return data; - }); + return options.thenCompose(attrs -> sendRequest("/client_log", attrs)) + .thenApply(result -> data); } private CompletableFuture requestVerificationCode(RegistrationResponse existsResponse, VerificationCodeError lastError) { @@ -195,12 +174,8 @@ private Entry[] getRequestVerificationCodeParameters(Registratio }; } - private CompletionStage onCodeRequestSent(RegistrationResponse existsResponse, VerificationCodeError lastError, HttpResponse result) { - if (result.statusCode() != HttpURLConnection.HTTP_OK) { - throw new RegistrationException(null, result.body()); - } - - var response = Json.readValue(result.body(), RegistrationResponse.class); + private CompletionStage onCodeRequestSent(RegistrationResponse existsResponse, VerificationCodeError lastError, String result) { + var response = Json.readValue(result, RegistrationResponse.class); if (response.status() == VerificationCodeStatus.SUCCESS) { return CompletableFuture.completedFuture(response); } @@ -210,7 +185,7 @@ private CompletionStage onCodeRequestSent(RegistrationResp case NO_ROUTES -> throw new RegistrationException(response, "You can only register numbers that are already on Whatsapp, if you need to register any numbers please contact me on Telegram @Auties00"); default -> { var newErrorReason = response.errorReason(); - Validate.isTrue(newErrorReason != lastError, () -> new RegistrationException(response, result.body())); + Validate.isTrue(newErrorReason != lastError, () -> new RegistrationException(response, result)); yield requestVerificationCode(existsResponse, newErrorReason); } }; @@ -221,17 +196,13 @@ public CompletableFuture sendVerificationCode() { .thenComposeAsync(code -> getRegistrationOptions(store, keys, true, Map.entry("code", normalizeCodeResult(code)))) .thenComposeAsync(attrs -> sendRequest("/register", attrs)) .thenComposeAsync(result -> { - if (result.statusCode() != HttpURLConnection.HTTP_OK) { - throw new RegistrationException(null, result.body()); - } - - var response = Json.readValue(result.body(), RegistrationResponse.class); + var response = Json.readValue(result, RegistrationResponse.class); if (response.status() == VerificationCodeStatus.SUCCESS) { saveRegistrationStatus(store, keys, true); return CompletableFuture.completedFuture(response); } - throw new RegistrationException(response, result.body()); + throw new RegistrationException(response, result); }); } @@ -250,54 +221,22 @@ private String normalizeCodeResult(String captcha) { return captcha.replaceAll("-", "").trim(); } - private CompletableFuture> sendRequest(String path, Map params) { - var request = createRequest(path, params); - return httpClient.sendAsync(request, BodyHandlers.ofString()).thenApply(result -> { - System.out.println(path + ": " + result.body()); - return result; - }); - } - - private HttpRequest createRequest(String path, Map params) { + private CompletableFuture sendRequest(String path, Map params) { var encodedParams = toFormParams(params); var userAgent = store.device().toUserAgent(store.version()); - if(store.device().platform().isKaiOs()) { - return HttpRequest.newBuilder() - .uri(URI.create("%s%s?%s".formatted(Whatsapp.MOBILE_KAIOS_REGISTRATION_ENDPOINT, path, encodedParams))) - .GET() - .header("User-Agent", userAgent) - .build(); - } - var keypair = SignalKeyPair.random(); - var key = Curve25519.sharedKey(Whatsapp.REGISTRATION_PUBLIC_KEY, keypair.privateKey()); + var key = Curve25519.sharedKey(REGISTRATION_PUBLIC_KEY, keypair.privateKey()); var buffer = AesGcm.encrypt(new byte[12], encodedParams.getBytes(StandardCharsets.UTF_8), key); var cipheredParameters = Base64.getUrlEncoder().encodeToString(Bytes.concat(keypair.publicKey(), buffer)); - var request = HttpRequest.newBuilder() - .uri(URI.create("%s%s?ENC=%s".formatted(Whatsapp.MOBILE_REGISTRATION_ENDPOINT, path, cipheredParameters))) - .GET() - .header("User-Agent", userAgent); - if(store.device().platform().isAndroid()) { - request.header("Accept", "text/json"); - request.header("WaMsysRequest", "1"); - request.header("request_token", UUID.randomUUID().toString()); - request.header("Content-Type", "application/x-www-form-urlencoded"); - } - - return request.build(); - } - - private HttpClient createClient() { - try { - var clientBuilder = HttpClient.newBuilder(); - store.proxy().ifPresent(proxy -> { - clientBuilder.proxy(ProxySelector.of(new InetSocketAddress(proxy.getHost(), proxy.getPort()))); - clientBuilder.authenticator(ProxyAuthenticator.globalAuthenticator()); - }); - return clientBuilder.build(); - }catch (Throwable exception) { - throw new RuntimeException(exception); - } + var android = store.device().platform().isAndroid(); + var headers = Attributes.of() + .put("User-Agent", userAgent) + .put("Accept", "text/json", android) + .put("WaMsysRequest", "1", android) + .put("request_token", UUID.randomUUID().toString(), android) + .put("Content-Type", "application/x-www-form-urlencoded", android) + .toMap(); + return httpClient.getString(URI.create("%s%s?ENC=%s".formatted(MOBILE_REGISTRATION_ENDPOINT, path, cipheredParameters)), headers); } @SafeVarargs @@ -313,20 +252,20 @@ private CompletableFuture> getRegistrationOptions(Store stor var result = Attributes.of() .put("cc", phoneNumber.countryCode().prefix()) .put("in", phoneNumber.numberWithoutPrefix()) - .put("rc", store.releaseChannel().index(), !store.device().platform().isKaiOs()) + .put("rc", store.releaseChannel().index()) .put("lg", phoneNumber.countryCode().lg()) .put("lc", phoneNumber.countryCode().lc()) .put("authkey", Base64.getUrlEncoder().encodeToString(keys.noiseKeyPair().publicKey())) .put("vname", certificate, certificate != null) .put("e_regid", Base64.getUrlEncoder().encodeToString(keys.encodedRegistrationId())) - .put("e_keytype", Base64.getUrlEncoder().encodeToString(Specification.Signal.KEY_BUNDLE_TYPE)) + .put("e_keytype", Base64.getUrlEncoder().encodeToString(SignalConstants.KEY_BUNDLE_TYPE)) .put("e_ident", Base64.getUrlEncoder().encodeToString(keys.identityKeyPair().publicKey())) .put("e_skey_id", Base64.getUrlEncoder().encodeToString(keys.signedKeyPair().encodedId())) .put("e_skey_val", Base64.getUrlEncoder().encodeToString(keys.signedKeyPair().publicKey())) .put("e_skey_sig", Base64.getUrlEncoder().encodeToString(keys.signedKeyPair().signature())) .put("fdid", keys.fdid().toLowerCase(Locale.ROOT), store.device().platform().isAndroid()) .put("fdid", keys.fdid().toUpperCase(Locale.ROOT), store.device().platform().isIOS()) - .put("expid", Base64.getUrlEncoder().encodeToString(keys.deviceId()), !store.device().platform().isKaiOs()) + .put("expid", Base64.getUrlEncoder().encodeToString(keys.deviceId())) .put("id", convertBufferToUrlHex(keys.identityId())) .put("token", token, useToken) .putAll(requiredAttributes) diff --git a/src/main/java/it/auties/whatsapp/socket/SocketSession.java b/src/main/java/it/auties/whatsapp/socket/SocketSession.java deleted file mode 100644 index b05ce07b6..000000000 --- a/src/main/java/it/auties/whatsapp/socket/SocketSession.java +++ /dev/null @@ -1,278 +0,0 @@ -package it.auties.whatsapp.socket; - -import it.auties.whatsapp.util.Exceptions; -import it.auties.whatsapp.util.ProxyAuthenticator; -import it.auties.whatsapp.util.Specification; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.net.*; -import java.net.http.HttpClient; -import java.net.http.WebSocket; -import java.nio.ByteBuffer; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionException; -import java.util.concurrent.CompletionStage; - -import static it.auties.whatsapp.util.Specification.Whatsapp.SOCKET_ENDPOINT; - -public abstract sealed class SocketSession permits SocketSession.WebSocketSession, SocketSession.RawSocketSession { - private static final int MESSAGE_LENGTH = 3; - static { - ProxyAuthenticator.allowAll(); - } - - final URI proxy; - SocketListener listener; - - private SocketSession(URI proxy) { - this.proxy = proxy; - } - - CompletableFuture connect(SocketListener listener) { - this.listener = listener; - return CompletableFuture.completedFuture(null); - } - - abstract void disconnect(); - - public abstract CompletableFuture sendBinary(byte[] bytes); - - static SocketSession of(URI proxy, boolean webSocket) { - if (webSocket) { - return new WebSocketSession(proxy); - } - - return new RawSocketSession(proxy); - } - - public static final class WebSocketSession extends SocketSession implements WebSocket.Listener { - private WebSocket session; - private byte[] message; - private int messageOffset; - - WebSocketSession(URI proxy) { - super(proxy); - } - - @SuppressWarnings("resource") // Not needed - @Override - CompletableFuture connect(SocketListener listener) { - if (session != null) { - return CompletableFuture.completedFuture(null); - } - - super.connect(listener); - return HttpClient.newBuilder() - .executor(command -> Thread.ofPlatform().start(command)) - .proxy(ProxySelector.of((InetSocketAddress) ProxyAuthenticator.getProxy(proxy).address())) - .authenticator(ProxyAuthenticator.globalAuthenticator()) - .build() - .newWebSocketBuilder() - .buildAsync(Specification.Whatsapp.WEB_SOCKET_ENDPOINT, this) - .thenAcceptAsync(webSocket -> { - this.session = webSocket; - listener.onOpen(this); - }) - .exceptionallyAsync(throwable -> { - if(throwable instanceof CompletionException && throwable.getCause() instanceof ConnectException) { - throw new RuntimeException("Cannot connect to Whatsapp: check your connection and whether it's available in your country"); - } - - Exceptions.rethrow(throwable); - return null; - }); - } - - @Override - void disconnect() { - if (session == null) { - return; - } - - session.sendClose(WebSocket.NORMAL_CLOSURE, ""); - } - - @Override - public CompletableFuture sendBinary(byte[] bytes) { - if (session == null) { - return CompletableFuture.completedFuture(null); - } - - return session.sendBinary(ByteBuffer.wrap(bytes), true); - } - - @Override - public CompletionStage onClose(WebSocket webSocket, int statusCode, String reason) { - message = null; - listener.onClose(); - session = null; - return WebSocket.Listener.super.onClose(webSocket, statusCode, reason); - } - - @Override - public void onError(WebSocket webSocket, Throwable error) { - listener.onError(error); - } - - @Override - public CompletionStage onBinary(WebSocket webSocket, ByteBuffer data, boolean last) { - if (message == null) { - var length = (data.get() << 16) | Short.toUnsignedInt(data.getShort()); - if(length < 0) { - return WebSocket.Listener.super.onBinary(webSocket, data, last); - } - - this.message = new byte[length]; - this.messageOffset = 0; - } - - var currentDataLength = data.remaining(); - var remainingDataLength = message.length - messageOffset; - var actualDataLength = Math.min(currentDataLength, remainingDataLength); - data.get(message, messageOffset, actualDataLength); - messageOffset += actualDataLength; - if (messageOffset != message.length) { - return WebSocket.Listener.super.onBinary(webSocket, data, last); - } - - notifyMessage(); - if(remainingDataLength - currentDataLength != 0) { - return onBinary(webSocket, data, true); - } - - - return WebSocket.Listener.super.onBinary(webSocket, data, last); - } - - private void notifyMessage() { - try { - listener.onMessage(message); - } catch (Throwable throwable) { - listener.onError(throwable); - }finally { - this.message = null; - } - } - } - - static final class RawSocketSession extends SocketSession { - static { - Authenticator.setDefault(ProxyAuthenticator.globalAuthenticator()); - } - - private Socket socket; - - RawSocketSession(URI proxy) { - super(proxy); - } - - @Override - CompletableFuture connect(SocketListener listener) { - if (isOpen()) { - return CompletableFuture.completedFuture(null); - } - - super.connect(listener); - return CompletableFuture.runAsync(() -> createConnection(listener)); - } - - private void createConnection(SocketListener listener) { - try { - this.socket = new Socket(ProxyAuthenticator.getProxy(proxy)); - socket.setKeepAlive(true); - socket.connect(proxy != null ? InetSocketAddress.createUnresolved(SOCKET_ENDPOINT.getHost(), SOCKET_ENDPOINT.getPort()) : new InetSocketAddress(SOCKET_ENDPOINT.getHost(), SOCKET_ENDPOINT.getPort())); - listener.onOpen(RawSocketSession.this); - readMessages(); - } catch (IOException exception) { - throw new UncheckedIOException(exception); - } - } - - private void readMessages() { - Thread.ofPlatform().start(() -> { - var lengthBytes = new byte[MESSAGE_LENGTH]; - int length; - while (isOpen()) { - try { - var lengthResult = readBytes(lengthBytes); - if(!lengthResult) { - break; - } - - length = (lengthBytes[0] << 16) | ((lengthBytes[1] & 0xFF) << 8) | (lengthBytes[2] & 0xFF); - if (length < 0) { - break; - } - - var messageBytes = new byte[length]; - var messageResult = readBytes(messageBytes); - if(!messageResult) { - break; - } - - listener.onMessage(messageBytes); - } catch (Throwable throwable) { - listener.onError(throwable); - } - } - - disconnect(); - }); - } - - private boolean readBytes(byte[] data) { - try { - var read = 0; - while (read != data.length) { - var chunk = socket.getInputStream().read(data, read, data.length - read); - if (chunk < 0) { - return false; - } - - read += chunk; - } - return true; - }catch (SocketException exception) { - return false; - } catch (IOException exception) { - throw new UncheckedIOException(exception); - } - } - - @Override - void disconnect() { - try { - if (socket == null) { - return; - } - - listener.onClose(); - socket.close(); - this.socket = null; - } catch (Throwable ignored) { - // No need to handle this - } - } - - private boolean isOpen() { - return socket != null && socket.isConnected(); - } - - @Override - public CompletableFuture sendBinary(byte[] bytes) { - if (socket == null) { - return CompletableFuture.completedFuture(null); - } - - return CompletableFuture.runAsync(() -> { - try { - socket.getOutputStream().write(bytes); - socket.getOutputStream().flush(); - } catch (Throwable throwable) { - throw new RuntimeException(throwable); - } - }); - } - } -} \ No newline at end of file diff --git a/src/main/java/it/auties/whatsapp/util/Bytes.java b/src/main/java/it/auties/whatsapp/util/Bytes.java index 03260690c..d786d90ac 100644 --- a/src/main/java/it/auties/whatsapp/util/Bytes.java +++ b/src/main/java/it/auties/whatsapp/util/Bytes.java @@ -17,7 +17,7 @@ import java.util.zip.Deflater; import java.util.zip.Inflater; -import static it.auties.whatsapp.util.Specification.Signal.CURRENT_VERSION; +import static it.auties.whatsapp.util.SignalConstants.CURRENT_VERSION; public final class Bytes { private static final String CROCKFORD_CHARACTERS = "123456789ABCDEFGHJKLMNPQRSTVWXYZ"; @@ -41,13 +41,13 @@ public static byte[] concat(List entries) { public static byte[] concat(byte[]... entries) { return Arrays.stream(entries) - .filter(Objects::nonNull) + .filter(entry -> entry != null && entry.length != 0) .reduce(Bytes::concat) .orElseGet(() -> new byte[0]); } public static byte[] concat(byte first, byte[] second) { - if (second == null) { + if (second == null || second.length == 0) { return new byte[]{first}; } @@ -58,11 +58,11 @@ public static byte[] concat(byte first, byte[] second) { } public static byte[] concat(byte[] first, byte[] second) { - if (first == null) { + if (first == null || first.length == 0) { return second; } - if (second == null) { + if (second == null || second.length == 0) { return first; } @@ -162,6 +162,14 @@ public static int bytesToInt(byte[] bytes, int length) { return result; } + public static int bytesToInt(ByteBuffer buffer, int length) { + var result = 0; + for (var i = 0; i < length; i++) { + result = 256 * result + Byte.toUnsignedInt(buffer.get()); + } + return result; + } + public static String bytesToCrockford(byte[] bytes) { var buffer = ByteBuffer.wrap(bytes); var value = 0; diff --git a/src/main/java/it/auties/whatsapp/util/ConcurrentLinkedHashedDequeue.java b/src/main/java/it/auties/whatsapp/util/ConcurrentLinkedHashedDequeue.java deleted file mode 100644 index 7a3c506fc..000000000 --- a/src/main/java/it/auties/whatsapp/util/ConcurrentLinkedHashedDequeue.java +++ /dev/null @@ -1,354 +0,0 @@ -package it.auties.whatsapp.util; - - -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Predicate; -import java.util.stream.Collectors; - -public class ConcurrentLinkedHashedDequeue extends AbstractQueue implements Deque { - private final AtomicReference> head; - private final AtomicReference> tail; - private final Set hashes; - - public ConcurrentLinkedHashedDequeue() { - this.head = new AtomicReference<>(null); - this.tail = new AtomicReference<>(null); - this.hashes = ConcurrentHashMap.newKeySet(); - } - - @Override - public void push(E e) { - add(e); - } - - @Override - public boolean offer(E e) { - return add(e); - } - - @Override - public boolean offerLast(E e) { - return add(e); - } - - @Override - public void addLast(E message) { - add(message); - } - - @Override - public boolean add(E e) { - var hash = Objects.hashCode(e); - if (hashes.contains(hash)) { - return false; - } - - var newNode = new Node<>(e); - var oldTail = tail.getAndSet(newNode); - if (oldTail == null) { - head.set(newNode); - } else { - oldTail.next = newNode; - newNode.prev = oldTail; - } - - hashes.add(hash); - return true; - } - - @Override - public boolean offerFirst(E e) { - addFirst(e); - return true; - } - - @Override - public void addFirst(E message) { - var hash = Objects.hashCode(message); - if (hashes.contains(hash)) { - return; - } - - var newNode = new Node<>(message); - var oldHead = head.getAndSet(newNode); - if (oldHead == null) { - tail.set(newNode); - } else { - oldHead.prev = newNode; - newNode.next = oldHead; - } - - hashes.add(hash); - } - - @Override - public E removeLast() { - return remove(); - } - - @Override - public boolean remove(Object o) { - var hash = Objects.hashCode(o); - if (!hashes.contains(hash)) { - return false; - } - - var node = head.get(); - while (node != null) { - if (node.item.equals(o)) { - removeNode(node, hash); - return true; - } - node = node.next; - } - - return false; - } - - @Override - public boolean removeAll(Collection collection) { - var hashCodes = collection.stream() - .map(Objects::hashCode) - .collect(Collectors.toUnmodifiableSet()); - var node = head.get(); - while (node != null) { - var hash = Objects.hashCode(node.item); - if (hashCodes.contains(hash)) { - removeNode(node, hash); - return true; - } - node = node.next; - } - - return false; - } - - @Override - public E poll() { - return remove(); - } - - @Override - public E pollLast() { - return remove(); - } - - @Override - public E remove() { - var tailItem = tail.get(); - if (tailItem == null) { - return null; - } - - var node = tail.getAndSet(tailItem.prev); - if (node == head.get()) { - head.compareAndSet(node, tailItem.prev); - } - - hashes.remove(Objects.hashCode(node.item)); - return node.item; - } - - private void removeNode(Node node, int hash) { - if (node == head.get()) { - var removed = head.getAndSet(head.get().next); - if (removed == tail.get()) { - tail.compareAndSet(removed, removed.prev); - } - } else if (node == tail.get()) { - var removed = tail.getAndSet(tail.get().prev); - if (removed == head.get()) { - head.compareAndSet(removed, tail.get().prev); - } - } else { - node.prev.next = node.next; - node.next.prev = node.prev; - } - hashes.remove(hash); - } - - @Override - public E pollFirst() { - return removeFirst(); - } - - @Override - public E pop() { - return removeFirst(); - } - - @Override - public E removeFirst() { - var node = head.getAndSet(head.get().next); - if (node == tail.get()) { - tail.compareAndSet(node, node.prev); - } - return node.item; - } - - @Override - public int size() { - return hashes.size(); - } - - @Override - public boolean isEmpty() { - return head.get() == null; - } - - @Override - public boolean contains(Object o) { - return hashes.contains(Objects.hashCode(o)); - } - - @Override - public Iterator iterator() { - return new Iterator<>() { - private Node nextNode = head.get(); - - @Override - public boolean hasNext() { - return nextNode != null; - } - - @Override - public E next() { - if (nextNode == null) { - throw new NoSuchElementException(); - } - - var item = nextNode.item; - nextNode = nextNode.next; - return item; - } - }; - } - - public Iterator descendingIterator() { - return new Iterator<>() { - private Node previousNode = tail.get(); - - @Override - public boolean hasNext() { - return previousNode != null; - } - - @Override - public E next() { - if (previousNode == null) { - throw new NoSuchElementException(); - } - - var item = previousNode.item; - previousNode = previousNode.prev; - return item; - } - }; - } - - - @Override - public E element() { - return peek(); - } - - @Override - public E peekFirst() { - return peek(); - } - - @Override - public E peek() { - var headItem = head.get(); - if (headItem == null) { - return null; - } - - return headItem.item; - } - - @Override - public E peekLast() { - var tailItem = tail.get(); - if (tailItem == null) { - return null; - } - - return tailItem.item; - } - - @Override - public E getFirst() { - var headItem = head.get(); - if (headItem == null) { - throw new NoSuchElementException(); - } - - return headItem.item; - } - - @Override - public E getLast() { - var tailItem = tail.get(); - if (tailItem == null) { - throw new NoSuchElementException(); - } - - return tailItem.item; - } - - @Override - public boolean removeFirstOccurrence(Object o) { - var node = head.get(); - while (node != null) { - if (node.item.equals(o)) { - var hash = Objects.hashCode(node.item); - removeNode(node, hash); - return true; - } - node = node.next; - } - return false; - } - - @Override - public boolean removeIf(Predicate filter) { - var node = tail.get(); - while (node != null) { - if (filter.test(node.item)) { - var hash = Objects.hashCode(node.item); - removeNode(node, hash); - return true; - } - node = node.prev; - } - - return false; - } - - @Override - public boolean removeLastOccurrence(Object o) { - var node = tail.get(); - while (node != null) { - if (node.item.equals(o)) { - var hash = Objects.hashCode(node.item); - removeNode(node, hash); - return true; - } - node = node.prev; - } - return false; - } - - private static class Node { - final E item; - Node next; - Node prev; - - Node(E item) { - this.item = item; - } - } -} diff --git a/src/main/java/it/auties/whatsapp/util/ConcurrentLinkedSet.java b/src/main/java/it/auties/whatsapp/util/ConcurrentLinkedSet.java new file mode 100644 index 000000000..86fb096d4 --- /dev/null +++ b/src/main/java/it/auties/whatsapp/util/ConcurrentLinkedSet.java @@ -0,0 +1,432 @@ +package it.auties.whatsapp.util; + + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +public class ConcurrentLinkedSet extends AbstractCollection implements Set, Deque { + private Node head; + private Node tail; + private final ReentrantLock lock; + private final Set hashes; + + public ConcurrentLinkedSet() { + this.hashes = ConcurrentHashMap.newKeySet(); + this.lock = new ReentrantLock(true); + } + + @Override + public void push(E e) { + add(e); + } + + @Override + public boolean offer(E e) { + return add(e); + } + + @Override + public boolean offerLast(E e) { + return add(e); + } + + @Override + public void addLast(E message) { + add(message); + } + + @Override + public boolean add(E e) { + try { + lock.lock(); + var hash = Objects.hashCode(e); + if (hashes.contains(hash)) { + return false; + } + + var newNode = new Node<>(e); + if (tail == null) { + head = newNode; + } else { + tail.next = newNode; + newNode.prev = tail; + } + tail = newNode; + hashes.add(hash); + return true; + } finally { + lock.unlock(); + } + } + + @Override + public boolean offerFirst(E e) { + addFirst(e); + return true; + } + + @Override + public void addFirst(E message) { + try { + lock.lock(); + var hash = Objects.hashCode(message); + if (hashes.contains(hash)) { + return; + } + + var newNode = new Node<>(message); + if (head == null) { + tail = newNode; + } else { + head.prev = newNode; + newNode.next = head; + } + head = newNode; + hashes.add(hash); + } finally { + lock.unlock(); + } + } + + @Override + public E removeLast() { + return remove(); + } + + @Override + public boolean remove(Object o) { + try { + lock.lock(); + var hash = Objects.hashCode(o); + if (!hashes.contains(hash)) { + return false; + } + + var node = head; + while (node != null) { + if (node.item.equals(o)) { + removeNode(node, hash); + return true; + } + node = node.next; + } + + return false; + } finally { + lock.unlock(); + } + } + + @Override + public boolean removeAll(Collection collection) { + try { + if (collection.isEmpty()) { + return true; + } + + lock.lock(); + var hashCodes = collection.stream() + .map(Objects::hashCode) + .collect(Collectors.toSet()); + var node = head; + while (node != null && !hashCodes.isEmpty()) { + var hash = Objects.hashCode(node.item); + if (hashCodes.remove(hash)) { + removeNode(node, hash); + } + node = node.next; + } + return hashCodes.isEmpty(); + } finally { + lock.unlock(); + } + } + + @Override + public boolean removeFirstOccurrence(Object o) { + try { + lock.lock(); + var node = head; + while (node != null) { + if (node.item.equals(o)) { + var hash = Objects.hashCode(node.item); + removeNode(node, hash); + return true; + } + node = node.next; + } + return false; + } finally { + lock.unlock(); + } + } + + @Override + public boolean removeIf(Predicate filter) { + try { + lock.lock(); + var node = tail; + while (node != null) { + if (filter.test(node.item)) { + var hash = Objects.hashCode(node.item); + removeNode(node, hash); + return true; + } + node = node.prev; + } + + return false; + } finally { + lock.unlock(); + } + } + + @Override + public boolean removeLastOccurrence(Object o) { + try { + lock.lock(); + var node = tail; + while (node != null) { + if (node.item.equals(o)) { + var hash = Objects.hashCode(node.item); + removeNode(node, hash); + return true; + } + node = node.prev; + } + return false; + } finally { + lock.unlock(); + } + } + + @Override + public E poll() { + return remove(); + } + + @Override + public E pollLast() { + return remove(); + } + + @Override + public E remove() { + try { + lock.lock(); + var tailItem = tail; + if (tailItem == null) { + return null; + } + + if (head == tail) { + head = tailItem.prev; + } + + var result = tail.item; + hashes.remove(Objects.hashCode(result)); + tail = tailItem.prev; + return result; + } finally { + lock.unlock(); + } + } + + private void removeNode(Node node, int hash) { + try { + lock.lock(); + if (node == head) { + if (head == tail) { + tail = head.prev; + } + head = head.next; + } else if (node == tail) { + tail = tail.next; + } else { + node.prev.next = node.next; + node.next.prev = node.prev; + } + hashes.remove(hash); + } finally { + lock.unlock(); + } + } + + @Override + public E pollFirst() { + return removeFirst(); + } + + @Override + public E pop() { + return removeFirst(); + } + + @Override + public E removeFirst() { + try { + lock.lock(); + if (head == tail) { + tail = head.prev; + } + + var result = head.item; + hashes.remove(Objects.hashCode(result)); + head = head.next; + return result; + } finally { + lock.unlock(); + } + } + + @Override + public int size() { + return hashes.size(); + } + + @Override + public boolean isEmpty() { + return head == null; + } + + @Override + public boolean contains(Object o) { + return hashes.contains(Objects.hashCode(o)); + } + + @Override + public Iterator iterator() { + return new Iterator<>() { + private Node nextNode = head; + + @Override + public boolean hasNext() { + return nextNode != null; + } + + @Override + public E next() { + if (nextNode == null) { + throw new NoSuchElementException(); + } + + var item = nextNode.item; + nextNode = nextNode.next; + return item; + } + }; + } + + public Iterator descendingIterator() { + return new Iterator<>() { + private Node previousNode = tail; + + @Override + public boolean hasNext() { + return previousNode != null; + } + + @Override + public E next() { + if (previousNode == null) { + throw new NoSuchElementException(); + } + + var item = previousNode.item; + previousNode = previousNode.prev; + return item; + } + }; + } + + + @Override + public E element() { + return peek(); + } + + @Override + public E peekFirst() { + return peek(); + } + + @Override + public E peek() { + try { + lock.lock(); + if (head == null) { + return null; + } + + return head.item; + } finally { + lock.unlock(); + } + } + + @Override + public E peekLast() { + try { + lock.lock(); + if (tail == null) { + return null; + } + + return tail.item; + } finally { + lock.unlock(); + } + } + + @Override + public E getFirst() { + try { + lock.lock(); + if (head == null) { + throw new NoSuchElementException(); + } + + return head.item; + } finally { + lock.unlock(); + } + } + + @Override + public E getLast() { + try { + lock.lock(); + if (tail == null) { + throw new NoSuchElementException(); + } + + return tail.item; + } finally { + lock.unlock(); + } + } + + @Override + public void clear() { + try { + lock.lock(); + head = tail = null; + hashes.clear(); + }finally { + lock.unlock(); + } + } + + private static class Node { + final E item; + Node next; + Node prev; + + Node(E item) { + this.item = item; + } + } +} diff --git a/src/main/java/it/auties/whatsapp/util/Exceptions.java b/src/main/java/it/auties/whatsapp/util/Exceptions.java index 6e8b4c75f..06f2e6e0e 100644 --- a/src/main/java/it/auties/whatsapp/util/Exceptions.java +++ b/src/main/java/it/auties/whatsapp/util/Exceptions.java @@ -14,8 +14,8 @@ public final class Exceptions { private static final Path DEFAULT_DIRECTORY = Path.of(System.getProperty("user.home") + "/.cobalt/errors"); - public static Throwable current(String message) { - var result = new RuntimeException(message); + public static RuntimeException current() { + var result = new RuntimeException(); result.setStackTrace(currentStackTrace()); return result; } diff --git a/src/main/java/it/auties/whatsapp/util/Json.java b/src/main/java/it/auties/whatsapp/util/Json.java index d1eaa4878..e4d4b44de 100644 --- a/src/main/java/it/auties/whatsapp/util/Json.java +++ b/src/main/java/it/auties/whatsapp/util/Json.java @@ -18,6 +18,7 @@ import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY; import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE; import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_DEFAULT; +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL; import static com.fasterxml.jackson.annotation.PropertyAccessor.*; import static com.fasterxml.jackson.databind.DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY; import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; @@ -26,6 +27,7 @@ public final class Json { private static final ObjectMapper json; + private static final ObjectWriter prettyWriter; static { try { @@ -36,13 +38,16 @@ public final class Json { .registerModule(new JavaTimeModule()) .registerModule(new ParameterNamesModule()) .registerModule(optionalModule) - .setSerializationInclusion(NON_DEFAULT) .enable(FAIL_ON_EMPTY_BEANS) .enable(ACCEPT_SINGLE_VALUE_AS_ARRAY) .disable(FAIL_ON_UNKNOWN_PROPERTIES) .setVisibility(ALL, ANY) .setVisibility(GETTER, NONE) + .setSerializationInclusion(NON_NULL) .setVisibility(IS_GETTER, NONE); + prettyWriter = json.copy() + .setSerializationInclusion(NON_DEFAULT) + .writerWithDefaultPrettyPrinter(); } catch (Throwable throwable) { var logger = System.getLogger("Json"); logger.log(ERROR, "An exception occurred while initializing json", throwable); @@ -64,7 +69,7 @@ public static String writeValueAsString(Object object) { public static String writeValueAsString(Object object, boolean pretty) { try { - var writer = pretty ? json.writerWithDefaultPrettyPrinter() : json.writer(); + var writer = pretty ? prettyWriter : json.writer(); return writer.writeValueAsString(object); } catch (IOException exception) { throw new UncheckedIOException("Cannot write json", exception); @@ -120,13 +125,13 @@ private OptionalDeserializer(JavaType optionalType) { public JsonDeserializer createContextual(DeserializationContext context, BeanProperty property) { if (property == null) { var optionalType = context.getContextualType(); - var valueType = optionalType.containedTypeOrUnknown(0); - return new OptionalDeserializer(valueType); + var mapValueType = optionalType.containedTypeOrUnknown(0); + return new OptionalDeserializer(mapValueType); } var optionalType = property.getType(); - var valueType = optionalType.containedTypeOrUnknown(0); - return new OptionalDeserializer(valueType); + var mapValueType = optionalType.containedTypeOrUnknown(0); + return new OptionalDeserializer(mapValueType); } @Override diff --git a/src/main/java/it/auties/whatsapp/util/Medias.java b/src/main/java/it/auties/whatsapp/util/Medias.java index 8fd4e0584..47a2a72b8 100644 --- a/src/main/java/it/auties/whatsapp/util/Medias.java +++ b/src/main/java/it/auties/whatsapp/util/Medias.java @@ -13,7 +13,8 @@ import it.auties.whatsapp.crypto.Sha256; import it.auties.whatsapp.exception.HmacValidationException; import it.auties.whatsapp.model.media.*; -import it.auties.whatsapp.util.Specification.Whatsapp; +import it.auties.whatsapp.model.node.Attributes; +import it.auties.whatsapp.net.HttpClient; import javax.imageio.ImageIO; import java.awt.*; @@ -21,14 +22,9 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.net.HttpURLConnection; import java.net.URI; import java.net.URLConnection; import java.net.URLEncoder; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.net.http.HttpResponse.BodyHandlers; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -37,16 +33,16 @@ import java.util.concurrent.CompletableFuture; import java.util.stream.IntStream; -import static java.net.http.HttpRequest.BodyPublishers.ofByteArray; -import static java.net.http.HttpResponse.BodyHandlers.ofString; - public final class Medias { + public static final String WEB_ORIGIN = "https://web.whatsapp.com"; + private static final String MOBILE_ANDROID_USER_AGENT = "Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.57 Mobile Safari/537.36"; private static final int WAVEFORM_SAMPLES = 64; private static final int PROFILE_PIC_SIZE = 640; private static final String DEFAULT_HOST = "mmg.whatsapp.net"; private static final int THUMBNAIL_SIZE = 32; private static volatile HttpClient httpClient; + private static final Object httpClientLock = new Object(); public static byte[] getProfilePic(byte[] file) { try { @@ -69,7 +65,7 @@ public static byte[] getProfilePic(byte[] file) { @SafeVarargs public static CompletableFuture downloadAsync(URI uri, Map.Entry... headers) { - return downloadAsync(uri, Whatsapp.MOBILE_ANDROID_USER_AGENT, headers); + return downloadAsync(uri, MOBILE_ANDROID_USER_AGENT, headers); } @SafeVarargs @@ -79,36 +75,28 @@ public static CompletableFuture downloadAsync(URI uri, String userAgent, return CompletableFuture.completedFuture(null); } - var request = HttpRequest.newBuilder() - .GET() - .uri(uri); - if (userAgent != null) { - request.header("User-Agent", userAgent); - } - for(var header : headers) { - request.header(header.getKey(), header.getValue()); - } - return getOrCreateClient().sendAsync(request.build(), BodyHandlers.ofByteArray()).thenCompose(response -> { - if (response.statusCode() != HttpURLConnection.HTTP_OK) { - return CompletableFuture.failedFuture(new IllegalArgumentException("Erroneous status code: " + response.statusCode())); - } - - return CompletableFuture.completedFuture(response.body()); - }); + var safeHeaders = Attributes.of(headers) + .put("User-Agent", userAgent, Objects::nonNull) + .toMap(); + return getOrCreateClient().getRaw(uri, safeHeaders); } catch (Throwable exception) { return CompletableFuture.failedFuture(exception); } } - private static synchronized HttpClient getOrCreateClient() { - if(httpClient != null) { - return httpClient; + private static HttpClient getOrCreateClient() { + var value = httpClient; + if (value == null) { + synchronized (httpClientLock) { + value = httpClient; + if (value == null) { + value = new HttpClient(HttpClient.Platform.DEFAULT, false); + httpClient = value; + } + } } - return httpClient = HttpClient.newBuilder() - .version(HttpClient.Version.HTTP_1_1) - .followRedirects(HttpClient.Redirect.ALWAYS) - .build(); + return value; } public static CompletableFuture upload(byte[] file, AttachmentType type, MediaConnection mediaConnection) { @@ -121,16 +109,14 @@ public static CompletableFuture upload(byte[] file, AttachmentType ty .withoutPadding() .encodeToString(Objects.requireNonNullElse(mediaFile.fileEncSha256(), mediaFile.fileSha256())); var uri = URI.create("https://%s/%s/%s?auth=%s&token=%s".formatted(DEFAULT_HOST, path, token, auth, token)); - var request = HttpRequest.newBuilder() - .POST(ofByteArray(Objects.requireNonNullElse(mediaFile.encryptedFile(), file))) - .uri(uri) - .header("Content-Type", "application/octet-stream") - .header("Accept", "application/json") - .header("Origin", Whatsapp.WEB_ORIGIN) - .build(); - return getOrCreateClient().sendAsync(request, ofString()).thenApplyAsync(response -> { - Validate.isTrue(response.statusCode() == 200, "Invalid status code: %s", response.statusCode()); - var upload = Json.readValue(response.body(), MediaUpload.class); + var headers = Map.of( + "Content-Type", "application/octet-stream", + "Accept", "application/json", + "Origin", WEB_ORIGIN + ); + var body = Objects.requireNonNullElse(mediaFile.encryptedFile(), file); + return getOrCreateClient().postRawWithoutSslParams(uri, headers, body).thenApplyAsync(response -> { + var upload = Json.readValue(response, MediaUpload.class); return new MediaFile( mediaFile.encryptedFile(), mediaFile.fileSha256(), @@ -169,15 +155,13 @@ public static CompletableFuture> downloadAsync(MutableAttachmen try { var url = provider.mediaUrl() .or(() -> provider.mediaDirectPath().map(Medias::createMediaUrl)) + .map(URI::create) .orElseThrow(() -> new NoSuchElementException("Missing url and path from media")); - var request = HttpRequest.newBuilder() - .uri(URI.create(url)) - .GET() - .build(); - return getOrCreateClient().sendAsync(request, HttpResponse.BodyHandlers.ofByteArray()) + return getOrCreateClient() + .getRaw(url) .thenApplyAsync(response -> handleResponse(provider, response)); } catch (Throwable error) { - return CompletableFuture.failedFuture(new RuntimeException("Cannot download media", error)); + return CompletableFuture.completedFuture(Optional.empty()); } } @@ -185,12 +169,7 @@ public static String createMediaUrl(String directPath) { return "https://%s%s".formatted(DEFAULT_HOST, directPath); } - private static Optional handleResponse(MutableAttachmentProvider provider, HttpResponse response) { - if (response.statusCode() == HttpURLConnection.HTTP_NOT_FOUND || response.statusCode() == HttpURLConnection.HTTP_GONE) { - return Optional.empty(); - } - - var body = response.body(); + private static Optional handleResponse(MutableAttachmentProvider provider, byte[] body) { var sha256 = Sha256.calculate(body); Validate.isTrue(provider.mediaEncryptedSha256().isEmpty() || Arrays.equals(sha256, provider.mediaEncryptedSha256().get()), "Cannot decode media: Invalid sha256 signature", SecurityException.class); diff --git a/src/main/java/it/auties/whatsapp/util/ProtobufFutureMixin.java b/src/main/java/it/auties/whatsapp/util/ProtobufFutureMixin.java deleted file mode 100644 index 3beb79925..000000000 --- a/src/main/java/it/auties/whatsapp/util/ProtobufFutureMixin.java +++ /dev/null @@ -1,18 +0,0 @@ -package it.auties.whatsapp.util; - -import it.auties.protobuf.annotation.ProtobufConverter; -import it.auties.protobuf.annotation.ProtobufMixin; - -import java.util.concurrent.CompletableFuture; - -public class ProtobufFutureMixin implements ProtobufMixin { - @ProtobufConverter - public static CompletableFuture of(T value) { - return CompletableFuture.completedFuture(value); - } - - @ProtobufConverter - public static T toValue(CompletableFuture uri) { - return uri.join(); - } -} diff --git a/src/main/java/it/auties/whatsapp/util/ProtobufUriMixin.java b/src/main/java/it/auties/whatsapp/util/ProtobufUriMixin.java deleted file mode 100644 index 1ea38ed99..000000000 --- a/src/main/java/it/auties/whatsapp/util/ProtobufUriMixin.java +++ /dev/null @@ -1,19 +0,0 @@ -package it.auties.whatsapp.util; - -import it.auties.protobuf.annotation.ProtobufConverter; -import it.auties.protobuf.annotation.ProtobufMixin; - -import java.net.URI; -import java.util.Objects; - -public class ProtobufUriMixin implements ProtobufMixin { - @ProtobufConverter - public static URI of(String uri) { - return uri == null ? null : URI.create(uri); - } - - @ProtobufConverter - public static String toValue(URI uri) { - return Objects.toString(uri); - } -} diff --git a/src/main/java/it/auties/whatsapp/util/ProtobufUuidMixin.java b/src/main/java/it/auties/whatsapp/util/ProtobufUuidMixin.java deleted file mode 100644 index 21ecbb811..000000000 --- a/src/main/java/it/auties/whatsapp/util/ProtobufUuidMixin.java +++ /dev/null @@ -1,19 +0,0 @@ -package it.auties.whatsapp.util; - -import it.auties.protobuf.annotation.ProtobufConverter; -import it.auties.protobuf.annotation.ProtobufMixin; - -import java.util.Objects; -import java.util.UUID; - -public class ProtobufUuidMixin implements ProtobufMixin { - @ProtobufConverter - public static UUID of(String uuid) { - return UUID.fromString(uuid); - } - - @ProtobufConverter - public static String toValue(UUID uuid) { - return Objects.toString(uuid); - } -} diff --git a/src/main/java/it/auties/whatsapp/util/Proxies.java b/src/main/java/it/auties/whatsapp/util/Proxies.java new file mode 100644 index 000000000..2494cb79e --- /dev/null +++ b/src/main/java/it/auties/whatsapp/util/Proxies.java @@ -0,0 +1,49 @@ +package it.auties.whatsapp.util; + +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.URI; +import java.util.Objects; +import java.util.OptionalInt; + +public final class Proxies { + public static Proxy toProxy(URI uri) { + if (uri == null) { + return Proxy.NO_PROXY; + } + + var scheme = Objects.requireNonNull(uri.getScheme(), "Invalid proxy, expected a scheme: %s".formatted(uri)); + var host = Objects.requireNonNull(uri.getHost(), "Invalid proxy, expected a host: %s".formatted(uri)); + var port = getDefaultPort(scheme, uri.getPort()).orElseThrow(() -> new NullPointerException("Invalid proxy, expected a port: %s".formatted(uri))); + return switch (scheme.toLowerCase()) { + case "http", "https" -> new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(host, port)); + case "socks5", "socks5h" -> new Proxy(Proxy.Type.SOCKS, InetSocketAddress.createUnresolved(host, port)); + default -> throw new IllegalStateException("Unexpected scheme: " + scheme); + }; + } + + private static OptionalInt getDefaultPort(String scheme, int port) { + return port != -1 ? OptionalInt.of(port) : switch (scheme.toLowerCase()) { + case "http" -> OptionalInt.of(80); + case "https" -> OptionalInt.of(443); + default -> OptionalInt.empty(); + }; + } + + public static UserInfo parseUserInfo(String userInfo) { + if(userInfo == null || userInfo.isEmpty()) { + return null; + } + + var data = userInfo.split(":", 2); + if(data.length > 2) { + throw new IllegalArgumentException("Invalid proxy authentication: " + userInfo); + } + + return new UserInfo(data[0], data.length == 2 ? data[1] : null); + } + + public record UserInfo(String username, String password) { + + } +} diff --git a/src/main/java/it/auties/whatsapp/util/ProxyAuthenticator.java b/src/main/java/it/auties/whatsapp/util/ProxyAuthenticator.java deleted file mode 100644 index 3505cd71a..000000000 --- a/src/main/java/it/auties/whatsapp/util/ProxyAuthenticator.java +++ /dev/null @@ -1,81 +0,0 @@ -package it.auties.whatsapp.util; - -import java.net.*; -import java.util.Map; -import java.util.Objects; -import java.util.OptionalInt; -import java.util.concurrent.ConcurrentHashMap; - -public class ProxyAuthenticator extends Authenticator { - private static volatile ProxyAuthenticator instance; - - static { - allowAll(); - } - - public static void allowAll() { - System.setProperty("jdk.http.auth.proxying.disabledSchemes", ""); - System.setProperty("jdk.http.auth.tunneling.disabledSchemes", ""); - } - - public static Proxy getProxy(URI uri) { - if (uri == null) { - return Proxy.NO_PROXY; - } - - var scheme = Objects.requireNonNull(uri.getScheme(), "Invalid proxy, expected a scheme: %s".formatted(uri)); - var host = Objects.requireNonNull(uri.getHost(), "Invalid proxy, expected a host: %s".formatted(uri)); - var port = getProxyPort(scheme, uri.getPort()).orElseThrow(() -> new NullPointerException("Invalid proxy, expected a port: %s".formatted(uri))); - return switch (scheme) { - case "http", "https" -> new Proxy(Proxy.Type.HTTP, InetSocketAddress.createUnresolved(host, port)); - case "socks5" -> new Proxy(Proxy.Type.SOCKS, InetSocketAddress.createUnresolved(host, port)); - default -> throw new IllegalStateException("Unexpected scheme: " + scheme); - }; - } - - private static OptionalInt getProxyPort(String scheme, int port) { - return port != -1 ? OptionalInt.of(port) : switch (scheme) { - case "http" -> OptionalInt.of(80); - case "https" -> OptionalInt.of(443); - default -> OptionalInt.empty(); - }; - } - - // We can't have a specialized authenticator as the Socket class doesn't provide a way to pass one - // Http(s) sockets under the hood use the HTTP(S)UrlConnection class, which supports setAuthenticator - // But Socks5 proxies invoke Authenticator#requestPasswordAuthentication which uses the default authenticator - // For now this is good enough, even though in a shared environment it could be a problem because two proxies could have the same host and port, but different users - public static ProxyAuthenticator globalAuthenticator() { - if(instance == null) { - instance = new ProxyAuthenticator(); - } - - return instance; - } - - private final Map credentials; - private ProxyAuthenticator() { - this.credentials = new ConcurrentHashMap<>(); - } - - public void register(URI uri) { - credentials.put("%s:%s".formatted(uri.getHost(), uri.getPort()), uri); - } - - public void unregister(URI uri) { - credentials.remove("%s:%s".formatted(uri.getHost(), uri.getPort())); - } - - @Override - protected PasswordAuthentication getPasswordAuthentication() { - var host = "%s:%s".formatted(getRequestingHost(), getRequestingPort()); - var info = credentials.get(host); - if (info == null) { - return super.getPasswordAuthentication(); - } - - var userInfo = info.getUserInfo().split(":", 2); - Validate.isTrue(userInfo.length == 2, "Invalid proxy credentials"); - return new PasswordAuthentication(userInfo[0], userInfo[1].toCharArray()); - } -} diff --git a/src/main/java/it/auties/whatsapp/util/SignalConstants.java b/src/main/java/it/auties/whatsapp/util/SignalConstants.java new file mode 100644 index 000000000..4028b552c --- /dev/null +++ b/src/main/java/it/auties/whatsapp/util/SignalConstants.java @@ -0,0 +1,16 @@ +package it.auties.whatsapp.util; + +public final class SignalConstants { + public static final int CURRENT_VERSION = 3; + public static final int IV_LENGTH = 16; + public static final int KEY_LENGTH = 32; + public static final int MAC_LENGTH = 8; + public static final int SIGNATURE_LENGTH = 64; + public static final int KEY_TYPE = 5; + public static final byte[] KEY_BUNDLE_TYPE = new byte[]{5}; + public static final int MAX_MESSAGES = 2000; + public static final String SKMSG = "skmsg"; + public static final String PKMSG = "pkmsg"; + public static final String MSG = "msg"; + public static final String UNAVAILABLE = "unavailable"; +} diff --git a/src/main/java/it/auties/whatsapp/util/Specification.java b/src/main/java/it/auties/whatsapp/util/Specification.java deleted file mode 100644 index 10e23e676..000000000 --- a/src/main/java/it/auties/whatsapp/util/Specification.java +++ /dev/null @@ -1,70 +0,0 @@ -package it.auties.whatsapp.util; - -import it.auties.whatsapp.binary.BinaryTokens; -import it.auties.whatsapp.model.signal.auth.Version; - -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.util.Base64; -import java.util.HexFormat; - -public class Specification { - public final static class Whatsapp { - public static final String DEFAULT_NAME = "Cobalt"; - public static final byte[] NOISE_PROTOCOL = "Noise_XX_25519_AESGCM_SHA256\0\0\0\0".getBytes(StandardCharsets.UTF_8); - public static final String WEB_ORIGIN = "https://web.whatsapp.com"; - public static final URI WEB_SOCKET_ENDPOINT = URI.create("wss://web.whatsapp.com/ws/chat"); - public static final URI SOCKET_ENDPOINT = URI.create("http://g.whatsapp.net:443"); - public static final String WEB_UPDATE_URL = "https://web.whatsapp.com/check-update?version=2.2245.9&platform=web"; - public static final String MOBILE_IOS_URL = "https://itunes.apple.com/lookup?bundleId=net.whatsapp.WhatsApp"; - public static final String MOBILE_IOS_USER_AGENT = "Mozilla/5.0 (iPhone; CPU iPhone OS 17_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3.1 Mobile/15E148 Safari/604.1"; - public static final String MOBILE_ANDROID_USER_AGENT = "Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.57 Mobile Safari/537.36"; - public static final String MOBILE_BUSINESS_IOS_URL = "https://itunes.apple.com/lookup?bundleId=net.whatsapp.WhatsAppSMB"; - public static final String MOBILE_REGISTRATION_ENDPOINT = "https://v.whatsapp.net/v2"; - public static final String MOBILE_KAIOS_REGISTRATION_ENDPOINT = "https://v-k.whatsapp.net/v2"; - public static final Version MOBILE_DEFAULT_PERSONAL_IOS_VERSION = Version.of("2.24.5.74"); - public static final Version MOBILE_DEFAULT_BUSINESS_IOS_VERSION = Version.of("2.24.4.78"); - public static final int GPIA_TOKEN_LENGTH = 430; - private static final byte[] WHATSAPP_VERSION_HEADER = "WA".getBytes(StandardCharsets.UTF_8); - private static final byte[] WEB_VERSION = new byte[]{6, BinaryTokens.DICTIONARY_VERSION}; - public static final byte[] WEB_PROLOGUE = Bytes.concat(WHATSAPP_VERSION_HEADER, WEB_VERSION); - private static final byte[] MOBILE_VERSION = new byte[]{5, BinaryTokens.DICTIONARY_VERSION}; - public static final byte[] MOBILE_PROLOGUE = Bytes.concat(WHATSAPP_VERSION_HEADER, MOBILE_VERSION); - public static final byte[] ACCOUNT_SIGNATURE_HEADER = {6, 0}; - public static final byte[] DEVICE_WEB_SIGNATURE_HEADER = {6, 1}; - public static final byte[] DEVICE_MOBILE_SIGNATURE_HEADER = {6, 2}; - public static final int MAX_COMPANIONS = 5; - public static final int THUMBNAIL_WIDTH = 480; - public static final int THUMBNAIL_HEIGHT = 339; - public static final byte[] REGISTRATION_PUBLIC_KEY = HexFormat.of().parseHex("8e8c0f74c3ebc5d7a6865c6c3c843856b06121cce8ea774d22fb6f122512302d"); - public static final String MOBILE_IOS_STATIC = "0a1mLfGUIBVrMKF1RdvLI5lkRBvof6vn0fD2QRSM"; - public static final String MOBILE_BUSINESS_IOS_STATIC = "USUDuDYDeQhY4RF2fCSp5m3F6kJ1M2J8wS7bbNA2"; - public static final int COMPANION_PAIRING_TIMEOUT = 10; - public static final int DEFAULT_HISTORY_SIZE = 59206; - public static final byte[][] CALL_RELAY = new byte[][]{ - new byte[]{-105, 99, -47, -29, 13, -106}, - new byte[]{-99, -16, -53, 62, 13, -106}, - new byte[]{-99, -16, -25, 62, 13, -106}, - new byte[]{-99, -16, -5, 62, 13, -106}, - new byte[]{-71, 60, -37, 62, 13, -106} - }; - public static final String BUSINESS_NAME_VCARD_PROPERTY = "X-WA-BIZ-NAME"; - public static final String PHONE_NUMBER_VCARD_PROPERTY = "WAID"; - public static final String DEFAULT_NUMBER_VCARD_TYPE = "CELL"; - } - - public final static class Signal { - public static final int CURRENT_VERSION = 3; - public static final int IV_LENGTH = 16; - public static final int KEY_LENGTH = 32; - public static final int MAC_LENGTH = 8; - public static final int SIGNATURE_LENGTH = 64; - public static final int KEY_TYPE = 5; - public static final byte[] KEY_BUNDLE_TYPE = new byte[]{5}; - public static final int MAX_MESSAGES = 2000; - public static final String SKMSG = "skmsg"; - public static final String PKMSG = "pkmsg"; - public static final String MSG = "msg"; - public static final String UNAVAILABLE = "unavailable"; - } -} diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 7d488e511..354df524e 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -9,7 +9,7 @@ requires com.google.zxing; requires com.google.zxing.javase; requires it.auties.qr; - requires java.desktop; + requires static java.desktop; // Serialization (Protobuf, JSON, Smile) requires it.auties.protobuf.base; @@ -21,9 +21,6 @@ requires com.fasterxml.jackson.datatype.jsr310; requires dd.plist; - // HTTP Client - requires java.net.http; - // Generate message previews requires it.auties.linkpreview; requires com.aspose.words; @@ -32,6 +29,10 @@ // Mobile api requires libphonenumber; + requires net.dongliu.apkparser; + + // Unsafe reflection + requires jdk.unsupported; exports it.auties.whatsapp.api; exports it.auties.whatsapp.controller; @@ -51,17 +52,14 @@ exports it.auties.whatsapp.model.payment; exports it.auties.whatsapp.model.message.button; exports it.auties.whatsapp.listener; - exports it.auties.whatsapp.util; exports it.auties.whatsapp.model.privacy; exports it.auties.whatsapp.model.poll; exports it.auties.whatsapp.model.mobile; exports it.auties.whatsapp.model.button.interactive; exports it.auties.whatsapp.model.button.base; - exports it.auties.whatsapp.model.button.misc; exports it.auties.whatsapp.model.button.template.hydrated; exports it.auties.whatsapp.model.button.template.hsm; exports it.auties.whatsapp.model.button.template; - exports it.auties.whatsapp.exception; exports it.auties.whatsapp.model.companion; exports it.auties.whatsapp.model.signal.session; exports it.auties.whatsapp.model.signal.auth; @@ -73,4 +71,5 @@ exports it.auties.whatsapp.model.button.template.highlyStructured; exports it.auties.whatsapp.model.jid; exports it.auties.whatsapp.model.newsletter; + exports it.auties.whatsapp.exception; } \ No newline at end of file diff --git a/src/main/resources/META-INF/services/javax.annotation.processing.AbstractProcessor b/src/main/resources/META-INF/services/javax.annotation.processing.AbstractProcessor index c6d841a0f..4adc1947d 100644 --- a/src/main/resources/META-INF/services/javax.annotation.processing.AbstractProcessor +++ b/src/main/resources/META-INF/services/javax.annotation.processing.AbstractProcessor @@ -1 +1 @@ -it.auties.whatsapp.listener.processor.RegisterListenerProcessor \ No newline at end of file +it.auties.whatsapp.listener.RegisterListenerProcessor \ No newline at end of file diff --git a/src/test/java/it/auties/whatsapp/TestLibrary.java b/src/test/java/it/auties/whatsapp/TestLibrary.java index 8423d433f..3d96a31cd 100644 --- a/src/test/java/it/auties/whatsapp/TestLibrary.java +++ b/src/test/java/it/auties/whatsapp/TestLibrary.java @@ -5,12 +5,12 @@ import it.auties.whatsapp.api.Whatsapp; import it.auties.whatsapp.listener.Listener; import it.auties.whatsapp.model.button.base.Button; +import it.auties.whatsapp.model.button.base.ButtonRow; +import it.auties.whatsapp.model.button.base.ButtonSection; import it.auties.whatsapp.model.button.base.ButtonText; import it.auties.whatsapp.model.button.interactive.InteractiveButton; import it.auties.whatsapp.model.button.interactive.InteractiveHeaderSimpleBuilder; import it.auties.whatsapp.model.button.interactive.InteractiveNativeFlowBuilder; -import it.auties.whatsapp.model.button.misc.ButtonRow; -import it.auties.whatsapp.model.button.misc.ButtonSection; import it.auties.whatsapp.model.button.template.hydrated.*; import it.auties.whatsapp.model.chat.*; import it.auties.whatsapp.model.companion.CompanionDevice; @@ -30,12 +30,16 @@ import it.auties.whatsapp.model.privacy.PrivacySettingType; import it.auties.whatsapp.model.sync.HistorySyncMessage; import it.auties.whatsapp.util.Bytes; +import it.auties.whatsapp.util.MediaUtils; import org.junit.jupiter.api.*; import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; import java.io.IOException; import java.time.ZonedDateTime; -import java.util.*; +import java.util.Collection; +import java.util.HexFormat; +import java.util.List; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -61,7 +65,8 @@ public class TestLibrary implements Listener { @BeforeAll public void init() throws IOException, InterruptedException { - loadConfig(); + contact = Jid.of(393495089819L); + account = SixPartsKeys.of("18102213505,s2ow3jfH20bwPwy3hzRlulkqxFdd2U2w9vvFS8iomCM=,6PH+z59udUs38+HhOoq5hVUd2z4QtKh8M8I3/MzYZmY=,BzImQolhr3JvNGHBbURtv6lDsL5j26M+yajF0CFApQk=,iLER4MzKf+teJmia2w54W5mBDWG4UAsMGChU0z2eyUw=,M6y/grjpjAeKkTqnRrvpbw=="); createApi(); createLatch(); latch.await(); @@ -79,22 +84,6 @@ private void createApi() { .exceptionally(Assertions::fail); } - private void loadConfig() throws IOException { - if (GithubActions.isActionsEnvironment()) { - log("Loading environment variables..."); - contact = Jid.of(System.getenv(GithubActions.CONTACT_NAME)); - account = SixPartsKeys.of(System.getenv(GithubActions.ACCOUNT)); - log("Loaded environment variables..."); - return; - } - - log("Loading configuration file..."); - var props = ConfigUtils.loadConfiguration(); - contact = Jid.of(Objects.requireNonNull(props.getProperty("contact"), "Missing contact property in config")); - account = SixPartsKeys.of(Objects.requireNonNull(props.getProperty("account", "Missing account property in config"))); - log("Loaded configuration file"); - } - private void createLatch() { latch = new CountDownLatch(3); } @@ -137,10 +126,7 @@ private void log(String message, Object... params) { } private Object[] redactParameters(Object... params) { - if (!GithubActions.isActionsEnvironment()) { - return params; - } - return Arrays.stream(params).map(entry -> "***").toArray(String[]::new); + return params; } @Test @@ -673,11 +659,6 @@ public void testPollMessage() { .votes(List.of(pollOptionFirst, pollOptionSecond)) .build(); api.sendMessage(contact, secondUpdate).join(); - var finalUpdate = new PollUpdateMessageSimpleBuilder() - .poll(pollInfo) - .votes(List.of()) - .build(); - api.sendMessage(contact, finalUpdate).join(); log("Sent poll message"); } diff --git a/src/test/java/it/auties/whatsapp/UpdateTokens.java b/src/test/java/it/auties/whatsapp/UpdateTokens.java index 126bc8ef5..6b08b7276 100644 --- a/src/test/java/it/auties/whatsapp/UpdateTokens.java +++ b/src/test/java/it/auties/whatsapp/UpdateTokens.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.core.json.JsonReadFeature; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; -import it.auties.whatsapp.util.Specification.Whatsapp; import java.io.IOException; import java.io.UncheckedIOException; @@ -31,6 +30,7 @@ public class UpdateTokens { private static final String DICTIONARY_ASSIGNMENT_REGEX = "\\.DICTIONARY_[0-9]_TOKEN=([a-z]);"; private static final String DICTIONARY_DECLARATION_REGEX = "const %s=\\[\"(.*?)\"]"; private static final String PROPS_REGEX = "\\.ABPropConfigs=\\{(.*?)]}},"; + public static final String WEB_ORIGIN = "https://web.whatsapp.com"; public static void main(String[] args) throws IOException, InterruptedException { System.out.println("Creating tokens class..."); @@ -119,10 +119,10 @@ private static String getSourceFile() throws IOException { } private static String getJavascriptSource() throws IOException, InterruptedException { - var whatsappRequest = createRequest(Whatsapp.WEB_ORIGIN); + var whatsappRequest = createRequest(WEB_ORIGIN); var whatsappResponse = HTTP_CLIENT.send(whatsappRequest, ofString()); var token = findResult(whatsappResponse.body(), TOKEN_REGEX); - var sourceRequest = createRequest("%s/app.%s.js".formatted(Whatsapp.WEB_ORIGIN, token)); + var sourceRequest = createRequest("%s/app.%s.js".formatted(WEB_ORIGIN, token)); return HTTP_CLIENT.send(sourceRequest, ofString()).body(); } diff --git a/src/test/java/it/auties/whatsapp/example/MobileLoginExample.java b/src/test/java/it/auties/whatsapp/example/MobileLoginExample.java index 74d1296ae..b74665a68 100644 --- a/src/test/java/it/auties/whatsapp/example/MobileLoginExample.java +++ b/src/test/java/it/auties/whatsapp/example/MobileLoginExample.java @@ -4,9 +4,7 @@ import it.auties.whatsapp.model.companion.CompanionDevice; import it.auties.whatsapp.model.mobile.SixPartsKeys; -import java.net.URI; import java.util.Scanner; -import java.util.concurrent.ThreadLocalRandom; public class MobileLoginExample { public static void main(String[] args) { @@ -21,7 +19,6 @@ public static void main(String[] args) { }; Whatsapp.mobileBuilder() .newConnection(SixPartsKeys.of(sixParts)) - .proxy(URI.create("http://wy961882248*4g_%s:999999@proxyus.rola.vip:1000/".formatted(ThreadLocalRandom.current().nextInt()))) .device(CompanionDevice.ios(business)) // Make sure to select the correct account type(business or personal) or you'll get error 401 .registered() .orElseThrow() diff --git a/src/test/java/it/auties/whatsapp/example/WebLoginExample.java b/src/test/java/it/auties/whatsapp/example/WebLoginExample.java index 4189d2f4a..b80684538 100644 --- a/src/test/java/it/auties/whatsapp/example/WebLoginExample.java +++ b/src/test/java/it/auties/whatsapp/example/WebLoginExample.java @@ -8,7 +8,7 @@ public class WebLoginExample { public static void main(String[] args) { Whatsapp.webBuilder() - .lastConnection() + .newConnection() .historyLength(WebHistoryLength.extended()) .unregistered(QrHandler.toTerminal()) .addLoggedInListener(api -> System.out.printf("Connected: %s%n", api.store().privacySettings())) @@ -21,7 +21,6 @@ public static void main(String[] args) { .addNodeSentListener(outgoing -> System.out.printf("Sent node %s%n", outgoing)) .addActionListener ((action, info) -> System.out.printf("New action: %s, info: %s%n", action, info)) .addSettingListener(setting -> System.out.printf("New setting: %s%n", setting)) - .addContactPresenceListener((chat, contact, status) -> System.out.printf("Status of %s changed in %s to %s%n", contact, chat.name(), status.name())) .addMessageStatusListener((info) -> System.out.printf("Message status update for %s%n", info.id())) .addChatMessagesSyncListener((api, chat, last) -> System.out.printf("%s now has %s messages: %s(oldest message: %s)%n", chat.name(), chat.messages().size(), !last ? "waiting for more" : "done", chat.oldestMessage().flatMap(ChatMessageInfo::timestamp).orElse(null))) .addDisconnectedListener(reason -> System.out.printf("Disconnected: %s%n", reason)) diff --git a/src/test/java/it/auties/whatsapp/routine/GenerateListenersLambdas.java b/src/test/java/it/auties/whatsapp/routine/GenerateListenersLambdas.java new file mode 100644 index 000000000..edfa9b757 --- /dev/null +++ b/src/test/java/it/auties/whatsapp/routine/GenerateListenersLambdas.java @@ -0,0 +1,46 @@ +package it.auties.whatsapp.routine; + +import it.auties.whatsapp.listener.Listener; + +import java.lang.reflect.Parameter; +import java.util.Arrays; +import java.util.stream.Collectors; + +public class GenerateListenersLambdas { + public static void main(String[] args) { + for(var method : Listener.class.getMethods()) { + var params = method.getParameters(); + var methodName = "add%sListener".formatted(method.getName().substring(2)); + var signature = "ListenerConsumer." + switch (params.length) { + case 0 -> "Empty consumer"; + case 1 -> "Unary<%s> consumer".formatted(params[0].getType().getSimpleName()); + case 2 -> "Binary<%s, %s> consumer".formatted(params[0].getType().getSimpleName(), params[1].getType().getSimpleName()); + case 3 -> "Ternary<%s, %s, %s> consumer".formatted(params[0].getType().getSimpleName(), params[1].getType().getSimpleName(), params[2].getType().getSimpleName()); + default -> throw new IllegalStateException("Unexpected value: " + params.length); + }; + var body = """ + addListener(new Listener() { + @Override + public void %s(%s) { + %s + } + });""".formatted( + method.getName(), + Arrays.stream(method.getParameters()) + .map(entry -> entry.getType().getSimpleName() + " " + entry.getName()) + .collect(Collectors.joining(", ")), + "consumer.accept(%s);".formatted( + Arrays.stream(method.getParameters()) + .map(Parameter::getName) + .collect(Collectors.joining(", ")) + ) + ); + System.out.printf(""" + public Whatsapp %s(%s) { + %s + return this; + }%n""", methodName, signature, body); + System.out.println(); + } + } +} diff --git a/src/test/java/it/auties/whatsapp/routine/UpdateTokens.java b/src/test/java/it/auties/whatsapp/routine/UpdateTokens.java new file mode 100644 index 000000000..69eda55d4 --- /dev/null +++ b/src/test/java/it/auties/whatsapp/routine/UpdateTokens.java @@ -0,0 +1,137 @@ +package it.auties.whatsapp.routine; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.json.JsonReadFeature; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import it.auties.whatsapp.util.Medias; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import static java.net.http.HttpResponse.BodyHandlers.ofString; + +public class UpdateTokens { + private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build(); + private static final String SOURCE_NAME = "BinaryTokens.java"; + private static final String TOKEN_REGEX = "