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 1d665908..7c344257 100644 --- a/src/main/java/it/auties/whatsapp/model/companion/CompanionDevice.java +++ b/src/main/java/it/auties/whatsapp/model/companion/CompanionDevice.java @@ -30,10 +30,10 @@ public static CompanionDevice web() { public static CompanionDevice ios(boolean business) { return new CompanionDevice( - "iPhone_X", + "iPhone_15_Pro_Max", "Apple", business ? PlatformType.IOS_BUSINESS : PlatformType.IOS, - "16.5.1" + "17.1.1" ); } 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 30643ece..597f9f3a 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 @@ -40,7 +40,7 @@ public ButtonsResponseMessage(String buttonId, String buttonText, ContextInfo co public static ButtonsResponseMessage of(ChatMessageInfo quoted, Button button) { return new ButtonsResponseMessageBuilder() .buttonId(button.id()) - .buttonText(button.bodyText().map(ButtonText::content)) + .buttonText(button.bodyText().map(ButtonText::content).orElse(null)) .contextInfo(ContextInfo.of(quoted)) .responseType(button.bodyType() == ButtonBody.Type.TEXT ? ResponseType.SELECTED_DISPLAY_TEXT : ResponseType.UNKNOWN) .build(); 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 bbdf3f89..8cccf85d 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 @@ -49,8 +49,8 @@ public InteractiveMessage(InteractiveHeader header, InteractiveBody body, Intera static InteractiveMessage simpleBuilder(InteractiveHeader header, String body, String footer, InteractiveMessageContent content, ContextInfo contextInfo) { var builder = new InteractiveMessageBuilder() .header(header) - .body(InteractiveBody.ofNullable(body)) - .footer(InteractiveFooter.ofNullable(footer)) + .body(InteractiveBody.ofNullable(body).orElse(null)) + .footer(InteractiveFooter.ofNullable(footer).orElse(null)) .contextInfo(contextInfo); switch (content) { case InteractiveShop interactiveShop -> builder.contentShop(interactiveShop); 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 2ab80de7..9610fc17 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 @@ -447,10 +447,10 @@ public Message content() { * * @return a non-null Optional ContextualMessage */ - public Optional contentWithContext() { + public Optional> contentWithContext() { return Optional.of(content()) .filter(entry -> entry instanceof ContextualMessage) - .map(entry -> (ContextualMessage) entry); + .map(entry -> (ContextualMessage) entry); } /** 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 aed6433e..c09e24f7 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 @@ -23,6 +23,10 @@ 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; 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 60f37d81..750c4c12 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 @@ -41,10 +41,10 @@ public final class PollCreationMessage implements ContextualMessage selectableOptionsMap; + final Map selectableOptionsMap; @ProtobufProperty(index = 1000, type = ProtobufType.OBJECT) - private final List selectedOptions; + final List selectedOptions; public PollCreationMessage(byte[] encryptionKey, String title, List selectableOptions, int selectableOptionsCount, ContextInfo contextInfo, Map selectableOptionsMap, List selectedOptions) { this.encryptionKey = encryptionKey; diff --git a/src/main/java/it/auties/whatsapp/model/response/IosVersionResponse.java b/src/main/java/it/auties/whatsapp/model/response/IosVersionResponse.java new file mode 100644 index 00000000..d503e9d8 --- /dev/null +++ b/src/main/java/it/auties/whatsapp/model/response/IosVersionResponse.java @@ -0,0 +1,36 @@ +package it.auties.whatsapp.model.response; + +import com.fasterxml.jackson.annotation.JsonCreator; +import it.auties.whatsapp.model.signal.auth.Version; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public 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); + } +} diff --git a/src/main/java/it/auties/whatsapp/registration/HttpRegistration.java b/src/main/java/it/auties/whatsapp/registration/HttpRegistration.java index b497a5d4..794f2b36 100644 --- a/src/main/java/it/auties/whatsapp/registration/HttpRegistration.java +++ b/src/main/java/it/auties/whatsapp/registration/HttpRegistration.java @@ -37,6 +37,10 @@ import java.util.stream.Collectors; public final class HttpRegistration { + static { + Authenticator.setDefault(new ProxyAuthenticator()); + } + private static final int TIMEOUT_SECONDS = 10; private final HttpClient httpClient; @@ -53,7 +57,7 @@ public HttpRegistration(Store store, Keys keys, AsyncVerificationCodeSupplier co this.method = method; this.httpClient = createClient(); var platform = store.device().platform(); - this.apnsClient = platform.isIOS() && method != VerificationCodeMethod.NONE ? new ApnsClient(store.proxy().orElse(null)) : null; + this.apnsClient = platform.isIOS() && method != VerificationCodeMethod.NONE ? new ApnsClient() : null; } public CompletableFuture registerPhoneNumber() { @@ -309,7 +313,6 @@ private Entry[] getRequestVerificationCodeParameters(ExistsRespo }; } - private CompletionStage onCodeRequestSent(ExistsResponse existsResponse, VerificationCodeError lastError, HttpResponse result) { if (result.statusCode() != HttpURLConnection.HTTP_OK) { throw new RegistrationException(null, result.body()); @@ -350,20 +353,6 @@ public CompletableFuture sendVerificationCode() { }); } - private String padCountryCodeValue(String inputString) { - if (inputString.length() >= 3) { - return inputString; - } - - var stringBuilder = new StringBuilder(); - while (stringBuilder.length() < 3 - inputString.length()) { - stringBuilder.append('0'); - } - - stringBuilder.append(inputString); - return stringBuilder.toString(); - } - private void saveRegistrationStatus(Store store, Keys keys, boolean registered) { keys.setRegistered(registered); if (registered) { diff --git a/src/main/java/it/auties/whatsapp/registration/TokenProvider.java b/src/main/java/it/auties/whatsapp/registration/TokenProvider.java index b782f19f..37c74b83 100644 --- a/src/main/java/it/auties/whatsapp/registration/TokenProvider.java +++ b/src/main/java/it/auties/whatsapp/registration/TokenProvider.java @@ -8,6 +8,7 @@ import it.auties.whatsapp.model.business.BusinessVerifiedNameCertificateSpec; import it.auties.whatsapp.model.business.BusinessVerifiedNameDetailsBuilder; import it.auties.whatsapp.model.business.BusinessVerifiedNameDetailsSpec; +import it.auties.whatsapp.model.response.IosVersionResponse; import it.auties.whatsapp.model.response.WebVersionResponse; import it.auties.whatsapp.model.signal.auth.UserAgent; import it.auties.whatsapp.model.signal.auth.UserAgent.PlatformType; @@ -53,9 +54,10 @@ public final class TokenProvider { } private static volatile Version webVersion; - private static volatile Version iosVersion; - private static volatile WhatsappApk cachedApk; - private static volatile WhatsappApk cachedBusinessApk; + private static volatile Version personalIosVersion; + private static volatile Version businessIosVersion; + private static volatile WhatsappApk personalApk; + private static volatile WhatsappApk businessApk; private static Path androidCache = Path.of(System.getProperty("user.home") + "/.cobalt/token/android"); @@ -72,33 +74,72 @@ public static CompletableFuture getVersion(UserAgent.PlatformType platf return getVersion(platform, true); } - private static CompletableFuture getVersion(UserAgent.PlatformType platform, boolean useJarCache) { + private static CompletableFuture getVersion(UserAgent.PlatformType platform, boolean useCache) { return switch (platform) { case WEB, WINDOWS, MACOS -> getWebVersion(); case ANDROID, ANDROID_BUSINESS -> - getAndroidData(platform.isBusiness(), useJarCache).thenApply(WhatsappApk::version); - case IOS, IOS_BUSINESS -> - CompletableFuture.completedFuture(platform.isBusiness() ? Whatsapp.DEFAULT_MOBILE_BUSINESS_IOS_VERSION : Whatsapp.DEFAULT_MOBILE_IOS_VERSION); // Fetching the latest ios version is harder than one might hope + getAndroidData(platform.isBusiness(), useCache).thenApply(WhatsappApk::version); + case IOS -> + getIosVersion(false); + case IOS_BUSINESS -> + getIosVersion(true); case KAIOS -> CompletableFuture.completedFuture(Whatsapp.DEFAULT_MOBILE_KAIOS_VERSION); default -> throw new IllegalStateException("Unsupported mobile os: " + platform); }; } - private static CompletableFuture getWebVersion() { + private static CompletableFuture getIosVersion(boolean business) { + if (business && businessIosVersion != null) { + return CompletableFuture.completedFuture(businessIosVersion); + } + + if (!business && personalIosVersion != null) { + return CompletableFuture.completedFuture(personalIosVersion); + } + try (var client = HttpClient.newHttpClient()) { - if (webVersion != null) { - return CompletableFuture.completedFuture(webVersion); - } + var request = HttpRequest.newBuilder() + .GET() + .header("User-Agent", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:120.0) Gecko/20100101 Firefox/120.0") + .uri(URI.create(business ? Whatsapp.MOBILE_BUSINESS_IOS_URL : Whatsapp.MOBILE_IOS_URL)) + .build(); + return client.sendAsync(request, ofString()) + .thenApplyAsync(response -> { + var result = Json.readValue(response.body(), IosVersionResponse.class) + .version() + .orElseThrow(); + if(business) { + businessIosVersion = result; + }else { + personalIosVersion = result; + } + System.out.println("Result: " + result); + + return result; + }); + } catch (Throwable throwable) { + throw new RuntimeException("Cannot fetch latest web version", throwable); + } + } + 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 -> Json.readValue(response.body(), WebVersionResponse.class)) - .thenApplyAsync(version -> webVersion = Version.of(version.currentVersion())); + .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); } @@ -165,12 +206,12 @@ private static String getAndroidToken(String phoneNumber, WhatsappApk whatsappDa } private static CompletableFuture getAndroidData(boolean business, boolean useJarCache) { - if (!business && cachedApk != null) { - return CompletableFuture.completedFuture(cachedApk); + if (!business && personalApk != null) { + return CompletableFuture.completedFuture(personalApk); } - if (business && cachedBusinessApk != null) { - return CompletableFuture.completedFuture(cachedBusinessApk); + if (business && businessApk != null) { + return CompletableFuture.completedFuture(businessApk); } return getCachedApk(business, useJarCache) @@ -179,7 +220,7 @@ private static CompletableFuture getAndroidData(boolean business, b } public static CompletableFuture downloadWhatsappApk(boolean business) { - return Medias.downloadAsync(business ? Whatsapp.MOBILE_BUSINESS_DOWNLOAD_URL : Whatsapp.MOBILE_DOWNLOAD_URL) + return Medias.downloadAsync(business ? Whatsapp.MOBILE_BUSINESS_ANDROID_URL : Whatsapp.MOBILE_ANDROID_URL) .thenApplyAsync(result -> getAndroidData(result, business)); } @@ -227,12 +268,12 @@ private static WhatsappApk getAndroidData(byte[] apk, boolean business) { if (business) { var result = new WhatsappApk(version, md5Hash, secretKey.getEncoded(), certificates, true); cacheWhatsappData(result); - return cachedBusinessApk = result; + return businessApk = result; } var result = new WhatsappApk(version, md5Hash, secretKey.getEncoded(), certificates, false); cacheWhatsappData(result); - return cachedApk = result; + return personalApk = result; } catch (IOException | GeneralSecurityException exception) { throw new RuntimeException("Cannot extract certificates from APK", exception); } diff --git a/src/main/java/it/auties/whatsapp/registration/apns/ApnsClient.java b/src/main/java/it/auties/whatsapp/registration/apns/ApnsClient.java index 2dfd8a8b..775d82fb 100644 --- a/src/main/java/it/auties/whatsapp/registration/apns/ApnsClient.java +++ b/src/main/java/it/auties/whatsapp/registration/apns/ApnsClient.java @@ -6,7 +6,6 @@ import it.auties.whatsapp.registration.SSLProvider; import it.auties.whatsapp.util.BytesHelper; import it.auties.whatsapp.util.Medias; -import it.auties.whatsapp.util.ProxyAuthenticator; import it.auties.whatsapp.util.Validate; import org.bouncycastle.asn1.x500.X500NameBuilder; import org.bouncycastle.asn1.x500.style.BCStyle; @@ -20,7 +19,10 @@ import javax.net.ssl.SSLSocket; import java.io.*; -import java.net.*; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.URI; +import java.net.URLEncoder; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; @@ -37,7 +39,6 @@ public class ApnsClient implements AutoCloseable { static { Security.addProvider(new BouncyCastleProvider()); - Authenticator.setDefault(new ProxyAuthenticator()); } private static final Pattern PROTOCOL_PATTERN = Pattern.compile("([^*]+)", Pattern.MULTILINE); @@ -45,7 +46,6 @@ public class ApnsClient implements AutoCloseable { private static final byte[] FAIRPLAY_CERT_CHAIN = Base64.getDecoder().decode("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"); private static final int PORT = 5223; - private final URI proxy; private final Set listeners; private SSLSocket socket; private byte[] certificate; @@ -54,8 +54,7 @@ public class ApnsClient implements AutoCloseable { private ExecutorService readerService; private ScheduledExecutorService keepAliveExecutor; - public ApnsClient(URI proxy) { - this.proxy = proxy; + public ApnsClient() { this.listeners = ConcurrentHashMap.newKeySet(); } diff --git a/src/main/java/it/auties/whatsapp/util/ProxyAuthenticator.java b/src/main/java/it/auties/whatsapp/util/ProxyAuthenticator.java index b9a87bdb..e22de8b3 100644 --- a/src/main/java/it/auties/whatsapp/util/ProxyAuthenticator.java +++ b/src/main/java/it/auties/whatsapp/util/ProxyAuthenticator.java @@ -56,6 +56,7 @@ protected PasswordAuthentication 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/Specification.java b/src/main/java/it/auties/whatsapp/util/Specification.java index 0415387e..f6316f91 100644 --- a/src/main/java/it/auties/whatsapp/util/Specification.java +++ b/src/main/java/it/auties/whatsapp/util/Specification.java @@ -18,10 +18,11 @@ public final static class Whatsapp { public static final String SOCKET_ENDPOINT = "g.whatsapp.net"; public static final int SOCKET_PORT = 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_ = "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_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 DEFAULT_MOBILE_IOS_VERSION = Version.of("2.23.25.71"); - public static final Version DEFAULT_MOBILE_BUSINESS_IOS_VERSION = Version.of("2.23.24.73"); public static final Version DEFAULT_MOBILE_KAIOS_VERSION = Version.of("2.2329.8"); public static final String APNS_WHATSAPP_BUSINESS_NAME = "net.whatsapp.WhatsAppSMB"; public static final String APNS_WHATSAPP_NAME = "net.whatsapp.WhatsApp"; @@ -42,8 +43,8 @@ public final static class Whatsapp { public static final int MAX_COMPANIONS = 5; public static final int THUMBNAIL_WIDTH = 480; public static final int THUMBNAIL_HEIGHT = 339; - public static final URI MOBILE_DOWNLOAD_URL = URI.create("https://www.whatsapp.com/android/current/WhatsApp.apk"); - public static final URI MOBILE_BUSINESS_DOWNLOAD_URL = URI.create("https://d.cdnpure.com/b/APK/com.whatsapp.w4b?version=latest"); + public static final URI MOBILE_ANDROID_URL = URI.create("https://www.whatsapp.com/android/current/WhatsApp.apk"); + public static final URI MOBILE_BUSINESS_ANDROID_URL = URI.create("https://d.cdnpure.com/b/APK/com.whatsapp.w4b?version=latest"); public static final byte[] MOBILE_ANDROID_SALT = Base64.getDecoder().decode("PkTwKSZqUfAUyR0rPQ8hYJ0wNsQQ3dW1+3SCnyTXIfEAxxS75FwkDf47wNv/c8pP3p0GXKR6OOQmhyERwx74fw1RYSU10I4r1gyBVDbRJ40pidjM41G1I1oN"); public static final byte[] REGISTRATION_PUBLIC_KEY = HexFormat.of().parseHex("8e8c0f74c3ebc5d7a6865c6c3c843856b06121cce8ea774d22fb6f122512302d"); public static final String MOBILE_IOS_STATIC = "0a1mLfGUIBVrMKF1RdvLI5lkRBvof6vn0fD2QRSM"; diff --git a/src/main/resources/token/android/whatsapp.json b/src/main/resources/token/android/whatsapp.json index 99019748..e4cb302f 100644 --- a/src/main/resources/token/android/whatsapp.json +++ b/src/main/resources/token/android/whatsapp.json @@ -1,6 +1,6 @@ { - "version" : "2.23.23.79", - "md5Hash" : "l1sljuU1IsPq8s4ow4fWoA==", + "version" : "2.24.1.71", + "md5Hash" : "wTh1ukw6PM9VZ/psR95rvg==", "secretKey" : "RFObk0NHtvEmCSluaRRbWDCd+U7QqKWi2UB4qOr/hwE+PZWmlkSqG5JGRlMsJ5+LzShVq1XyyLwWk623gAyI/w==", "certificates" : [ "MIIDMjCCAvCgAwIBAgIETCU2pDALBgcqhkjOOAQDBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENsYXJhMRYwFAYDVQQKEw1XaGF0c0FwcCBJbmMuMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEUMBIGA1UEAxMLQnJpYW4gQWN0b24wHhcNMTAwNjI1MjMwNzE2WhcNNDQwMjE1MjMwNzE2WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExFjAUBgNVBAoTDVdoYXRzQXBwIEluYy4xFDASBgNVBAsTC0VuZ2luZWVyaW5nMRQwEgYDVQQDEwtCcmlhbiBBY3RvbjCCAbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDRGYtLgWh7zyRtQainJfCpiaUbzjJuhMgo4fVWZIvXHaSHBU1t5w//S0lDK2hiqkj8KpMWGywVov9eZxZy37V26dEqr/c2m5qZ0E+ynSu7sqUD7kGx/zeIcGT0H+KAVgkGNQCo5Uc0koLRWYHNtYoIvt5R3X6YZylbPftF/8ayWTALBgcqhkjOOAQDBQADLwAwLAIUAKYCp0d6z4QQdyN74JDfQ2WCyi8CFDUM4CaNB+ceVXdKtOrNTQcc0e+t" ] } \ No newline at end of file diff --git a/src/main/resources/token/android/whatsapp_business.json b/src/main/resources/token/android/whatsapp_business.json index 7054d115..9270572b 100644 --- a/src/main/resources/token/android/whatsapp_business.json +++ b/src/main/resources/token/android/whatsapp_business.json @@ -1,6 +1,6 @@ { - "version" : "2.23.24.22", - "md5Hash" : "pqq9BfGHU8rF24+t+DhcNQ==", + "version" : "2.24.1.71", + "md5Hash" : "37WFLfzQs4n2k8DdCSnt0w==", "secretKey" : "VROA1coOL6M5ywTDPnPB/6CwjpIl2UjqEbIDpuf4TtgbPMj9sEhhi3gqtaG1PM/Jy4VODs6UQE7SMLcqzf/XVQ==", "certificates" : [ "MIIDMjCCAvCgAwIBAgIETCU2pDALBgcqhkjOOAQDBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENsYXJhMRYwFAYDVQQKEw1XaGF0c0FwcCBJbmMuMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEUMBIGA1UEAxMLQnJpYW4gQWN0b24wHhcNMTAwNjI1MjMwNzE2WhcNNDQwMjE1MjMwNzE2WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExFjAUBgNVBAoTDVdoYXRzQXBwIEluYy4xFDASBgNVBAsTC0VuZ2luZWVyaW5nMRQwEgYDVQQDEwtCcmlhbiBBY3RvbjCCAbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDRGYtLgWh7zyRtQainJfCpiaUbzjJuhMgo4fVWZIvXHaSHBU1t5w//S0lDK2hiqkj8KpMWGywVov9eZxZy37V26dEqr/c2m5qZ0E+ynSu7sqUD7kGx/zeIcGT0H+KAVgkGNQCo5Uc0koLRWYHNtYoIvt5R3X6YZylbPftF/8ayWTALBgcqhkjOOAQDBQADLwAwLAIUAKYCp0d6z4QQdyN74JDfQ2WCyi8CFDUM4CaNB+ceVXdKtOrNTQcc0e+t" ], "business" : true diff --git a/src/test/java/it/auties/whatsapp/ci/WebTest.java b/src/test/java/it/auties/whatsapp/ci/WebTest.java index 3f23bde7..f4c04b00 100644 --- a/src/test/java/it/auties/whatsapp/ci/WebTest.java +++ b/src/test/java/it/auties/whatsapp/ci/WebTest.java @@ -726,7 +726,7 @@ public void testContactMessage() { ADR:123 Main Street; Springfield; IL; 12345; USA END:VCARD """); - var contactMessage = new ContactMessage("John Doe", vcard, Optional.empty()); + var contactMessage = new ContactMessage("John Doe", vcard, null); var response = api.sendMessage(contact, contactMessage).join(); log("Sent contact: %s", response); } diff --git a/src/test/java/it/auties/whatsapp/local/MobileRunner.java b/src/test/java/it/auties/whatsapp/local/MobileRunner.java index fb0353fa..53ec8ab3 100644 --- a/src/test/java/it/auties/whatsapp/local/MobileRunner.java +++ b/src/test/java/it/auties/whatsapp/local/MobileRunner.java @@ -4,37 +4,74 @@ import it.auties.whatsapp.model.companion.CompanionDevice; import it.auties.whatsapp.model.mobile.VerificationCodeMethod; +import java.io.IOException; import java.net.URI; -import java.util.Scanner; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; public class MobileRunner { - public static void main(String[] args) { - var scanner = new Scanner(System.in); - System.out.println("Enter the phone number(with no +, spaces or parenthesis):"); - var phoneNumber = scanner.nextLong(); - var whatsapp = Whatsapp.mobileBuilder() - .newConnection() - .proxy(URI.create("http://wy961882248_1:999999@gate8.rola.vip:1066/")) - .device(CompanionDevice.ios(false)) - .unregistered() - .verificationCodeSupplier(MobileRunner::onScanCode) - .verificationCodeMethod(VerificationCodeMethod.SMS) - .register(phoneNumber) - .join(); - System.out.println(whatsapp.keys().toString()); - whatsapp.addLoggedInListener(() -> System.out.println("Connected")) - .connect() - .join(); - CompletableFuture.delayedExecutor(10, TimeUnit.SECONDS) - .execute(() -> whatsapp.disconnect().join()); + public static void main(String[] args) throws IOException, InterruptedException { + try(var client = HttpClient.newHttpClient()) { + var phoneNumberRequest = HttpRequest.newBuilder() + .uri(URI.create("https://daisysms.com/stubs/handler_api.php?api_key=dQc1wqtHwm0M6mFs6wmRBw8VK7nElD&action=getNumber&service=ds&max_price=0.50")) + .GET() + .build(); + var phoneNumberResponse = client.send(phoneNumberRequest, HttpResponse.BodyHandlers.ofString()); + var phoneNumberResponsePayload = phoneNumberResponse.body().split(":", 3); + if(!Objects.equals(phoneNumberResponsePayload[0], "ACCESS_NUMBER")) { + System.err.println(phoneNumberResponse.body()); + return; + } + + var id = phoneNumberResponsePayload[1]; + var phoneNumber = Long.parseLong(phoneNumberResponsePayload[2]); + System.out.println("Got phone number: " + phoneNumber); + var whatsapp = Whatsapp.mobileBuilder() + .newConnection() + .proxy(URI.create("http://wy961882248_1:999999@gate8.rola.vip:1066/")) + .device(CompanionDevice.ios(false)) + .unregistered() + .verificationCodeSupplier(() -> getOtp(client, id)) + .verificationCodeMethod(VerificationCodeMethod.SMS) + .register(phoneNumber) + .join(); + System.out.println(whatsapp.keys().toString()); + whatsapp.addLoggedInListener(() -> System.out.println("Connected")) + .connect() + .join(); + CompletableFuture.delayedExecutor(10, TimeUnit.SECONDS) + .execute(() -> whatsapp.disconnect().join()); + } + } + + private static CompletableFuture getOtp(HttpClient client, String id) { + var otpRequest = HttpRequest.newBuilder() + .uri(URI.create("https://daisysms.com/stubs/handler_api.php?api_key=dQc1wqtHwm0M6mFs6wmRBw8VK7nElD&action=getStatus&id=" + id)) + .GET() + .build(); + return client.sendAsync(otpRequest, HttpResponse.BodyHandlers.ofString()).thenCompose(result -> { + var otpResponsePayload = result.body().split(":", 2); + if(!Objects.equals(otpResponsePayload[0], "ACCESS_NUMBER")) { + System.out.println("Waiting otp: " + result.body()); + waitOtp(); + return getOtp(client, id); + } + + System.out.println("Got otp: " + otpResponsePayload[1]); + return CompletableFuture.completedFuture(otpResponsePayload[1]); + }); } - private static CompletableFuture onScanCode() { - System.out.println("Enter OTP: "); - var scanner = new Scanner(System.in); - return CompletableFuture.completedFuture(scanner.nextLine()); + private static void waitOtp() { + try { + Thread.sleep(3000L); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } } }