Скачивание профилей с dark.shopping, вход в аккаунты, сбор cookies и POT

This commit is contained in:
2025-05-31 13:02:33 +03:00
parent 5a00f35bbc
commit f1421992b6
59 changed files with 2124 additions and 148 deletions

0
.gitignore.txt Normal file
View File

View File

@ -0,0 +1 @@
markarenderwrvekx@gmail.com:seFFX1LvcMp4:arturkozlov99w5f1b2@mail.com:password

0
chromedriver Normal file
View File

30
pom.xml
View File

@ -63,6 +63,13 @@
<version>${jackson.version}</version>
</dependency>
<!-- Зависимость для поддержки Java Time API в Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- HTTP клиент для API запросов -->
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
@ -112,6 +119,29 @@
<artifactId>selenium-devtools-v135</artifactId>
<version>${selenium.version}</version>
</dependency>
<!-- Зависимости для работы с почтой -->
<dependency>
<groupId>jakarta.mail</groupId>
<artifactId>jakarta.mail-api</artifactId>
<version>2.1.3</version> <!-- Используйте актуальную версию -->
</dependency>
<dependency>
<groupId>org.eclipse.angus</groupId>
<artifactId>angus-mail</artifactId>
<version>2.0.3</version> <!-- Используйте актуальную версию -->
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
<version>3.42.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>

View File

@ -0,0 +1,495 @@
package com.google.accountgen;
import com.google.accountgen.config.AppProperties;
import com.google.accountgen.cookies.CookieManager;
import com.google.accountgen.proxy.ProxyManager;
import com.google.accountgen.service.AccountManager;
import com.google.accountgen.service.DarkShoppingService;
import com.google.accountgen.service.EmailService;
import com.google.accountgen.service.TwoCaptchaService;
import com.google.accountgen.youtube.YouTubeMusicHandler;
import com.google.accountgen.login.GoogleLoginHandler;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.*;
import org.openqa.selenium.logging.LogType;
import org.openqa.selenium.logging.LoggingPreferences;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.chrome.ChromeDriver;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Collections;
import java.util.Objects;
import java.util.Optional;
import java.util.logging.Level;
import java.io.File;
@Component
public class AccountProcessingService {
private static final Logger logger = LoggerFactory.getLogger(AccountProcessingService.class);
private final AppProperties appProperties;
private final DarkShoppingService darkShoppingService;
private final AccountManager accountManager;
private final EmailService emailService;
private final ProxyManager proxyManager;
private final CookieManager cookieManager;
private final TwoCaptchaService twoCaptchaService;
private String chosenProxy;
public AccountProcessingService(AppProperties appProperties, ProxyManager proxyManager, TwoCaptchaService twoCaptchaService) {
this.appProperties = appProperties;
this.proxyManager = proxyManager;
this.cookieManager = new CookieManager(appProperties.getCookies().getDirectory());
this.twoCaptchaService = twoCaptchaService;
this.darkShoppingService = new DarkShoppingService(
appProperties.getDarkshopping().getApiKey(),
appProperties.getAccounts().getDirectory()
);
this.accountManager = new AccountManager(
appProperties.getAccounts().getDirectory(),
this.darkShoppingService,
appProperties.getDarkshopping().getProductId(),
appProperties.getDarkshopping().getPurchaseQuantity()
);
if (appProperties.getImap() != null && appProperties.getImap().getHost() != null && !appProperties.getImap().getHost().isEmpty() && !appProperties.getImap().getHost().equals("imap.example.com")) {
this.emailService = new EmailService(
appProperties.getImap().getHost(),
appProperties.getImap().getPort(),
appProperties.getImap().getUsername(),
appProperties.getImap().getPassword(),
appProperties.getImap().isSsl()
);
logger.info("IMAP service configured for host: {}", appProperties.getImap().getHost());
} else {
this.emailService = null;
logger.info("IMAP service is not configured or uses default placeholder values.");
}
WebDriverManager.chromedriver().setup();
if (appProperties.isUseProxy()) {
logger.info("Proxy usage is enabled. Initializing ProxyManager...");
this.proxyManager.init();
bindProxy6ToIp();
}
}
private WebDriver setupChromeDriver(String accountEmailForCookies) {
logger.info("Настройка и запуск обычного ChromeDriver");
Path tempProfile = null;
WebDriver driver = null;
try {
ChromeOptions options = new ChromeOptions();
options.setPageLoadStrategy(PageLoadStrategy.EAGER);
options.addArguments("--user-agent=" + com.google.accountgen.service.FingerprintUtils.getRandomUserAgent());
String windowSize = com.google.accountgen.service.FingerprintUtils.getRandomWindowSize();
options.addArguments("--window-size=" + windowSize);
options.addArguments("--lang=" + com.google.accountgen.service.FingerprintUtils.getRandomLang());
tempProfile = Files.createTempDirectory("chrome-profile-");
options.addArguments("--user-data-dir=" + tempProfile.toAbsolutePath());
logger.info("Используется временный профиль Chrome: {}", tempProfile);
LoggingPreferences logPrefs = new LoggingPreferences();
logPrefs.enable(LogType.BROWSER, Level.ALL);
logPrefs.enable(LogType.PERFORMANCE, Level.OFF);
options.setCapability("goog:loggingPrefs", logPrefs);
options.addArguments("--no-sandbox");
options.addArguments("--disable-dev-shm-usage");
options.addArguments("--disable-extensions");
options.addArguments("--disable-blink-features=AutomationControlled");
options.addArguments("--disable-notifications");
options.addArguments("--disable-popup-blocking");
options.addArguments("--log-level=3");
options.addArguments("--silent");
options.addArguments("--disable-infobars");
// Удаление "Chrome is being controlled by automated test software"
options.setExperimentalOption("excludeSwitches", Collections.singletonList("enable-automation"));
options.setExperimentalOption("useAutomationExtension", false);
if (appProperties.isUseProxy() && proxyManager.hasProxies()) {
chosenProxy = proxyManager.getNextProxy();
if (chosenProxy != null) {
org.openqa.selenium.Proxy httpProxy = new org.openqa.selenium.Proxy();
httpProxy.setHttpProxy(chosenProxy).setSslProxy(chosenProxy);
options.setCapability(CapabilityType.PROXY, httpProxy);
logger.info("HTTP(S) proxy настроен: {}", chosenProxy);
} else {
logger.error("Не удалось получить прокси от ProxyManager, хотя прокси включены и должны быть доступны.");
}
} else if (appProperties.isUseProxy()) {
logger.warn("Прокси включены в конфигурации, но ProxyManager не имеет доступных прокси.");
}
logger.info("Создаём обычный ChromeDriver");
driver = new ChromeDriver(options);
logger.info("Attempting to load cookies for account: {}", accountEmailForCookies);
driver.get("https://www.google.com");
randomSleep(500, 1500);
cookieManager.loadCookiesFromJson(driver, accountEmailForCookies);
logger.info("Cookies loaded (if any). Proceeding with navigator properties override.");
org.openqa.selenium.JavascriptExecutor js = (org.openqa.selenium.JavascriptExecutor) driver;
js.executeScript("Object.defineProperty(navigator, 'webdriver', {get: () => undefined});");
js.executeScript("Object.defineProperty(navigator, 'languages', {get: () => ['en-US', 'en']});");
js.executeScript("Object.defineProperty(navigator, 'plugins', {get: () => [1,2,3,4,5]});");
js.executeScript("Intl.DateTimeFormat.prototype.resolvedOptions = function() { return { timeZone: '" + com.google.accountgen.service.FingerprintUtils.getRandomTimezone() + "' }; };");
logger.info("Экземпляр ChromeDriver успешно создан.");
return driver;
} catch (Exception e) {
logger.error("Критическая ошибка при настройке или запуске ChromeDriver", e);
if (driver != null) {
try { driver.quit(); } catch (Exception dq) { logger.error("Error quitting driver during setup error handling", dq); }
}
if (tempProfile != null) {
deleteTempProfileDirectory(tempProfile);
}
throw new RuntimeException("Не удалось инициализировать ChromeDriver", e);
}
}
@EventListener(ApplicationReadyEvent.class)
public void processAccounts() {
logger.info("Starting account processing...");
Optional<AccountManager.AccountDetails> accountDetailsOpt = accountManager.getNextAccount();
if (accountDetailsOpt.isEmpty()) {
logger.warn("No accounts available to process. Exiting.");
return;
}
AccountManager.AccountDetails accountDetails = accountDetailsOpt.get();
logger.info("Processing account: {} with proxy: {}", accountDetails.email(), chosenProxy != null ? chosenProxy : "none");
WebDriver driver = setupChromeDriver(accountDetails.email());
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
// Интенсивный прогрев перед основной логикой
performIntensiveWarmup(driver, wait, accountDetails.email());
YouTubeMusicHandler musicHandler = new YouTubeMusicHandler(driver);
GoogleLoginHandler loginHandler = new GoogleLoginHandler(driver, wait, twoCaptchaService);
Path tempProfilePath = null;
if (driver instanceof HasCapabilities) {
Capabilities capabilities = ((HasCapabilities) driver).getCapabilities();
String chromeUserDataDirArg = capabilities.getCapability(ChromeOptions.CAPABILITY).toString();
if (chromeUserDataDirArg.contains("user-data-dir=")) {
try {
String pathStr = chromeUserDataDirArg.split("user-data-dir=")[1].split(",")[0].trim();
tempProfilePath = Path.of(pathStr);
} catch (Exception e) {
logger.warn("Could not parse temp profile path from capabilities: {}", chromeUserDataDirArg, e);
}
}
} else {
logger.warn("Driver does not implement HasCapabilities, cannot get user-data-dir.");
}
try {
loginHandler.navigateToYouTubeMusicAndHandlePopups();
loginHandler.clickSignInOnYouTubeMusic();
loginHandler.performGoogleLogin(accountDetails);
loginHandler.handlePostLoginPopups();
wait.until(ExpectedConditions.or(
ExpectedConditions.urlContains("music.youtube.com"),
ExpectedConditions.visibilityOfElementLocated(By.xpath("//ytmusic-popup-container//ytmusic-settings-button | //a[@aria-label='Account'] | //yt-img-shadow[@class='style-scope ytmusic-nav-bar no-transition' and @height='32']"))
));
String currentUrl = driver.getCurrentUrl();
logger.info("Current URL after login attempt: {}", currentUrl);
boolean loggedInSuccessfully = false;
if (currentUrl.contains("music.youtube.com")) {
try {
wait.until(ExpectedConditions.visibilityOfElementLocated(
By.xpath("//ytmusic-popup-container//ytmusic-settings-button | //a[@aria-label='Account'] | //yt-img-shadow[@class='style-scope ytmusic-nav-bar no-transition' and @height='32']")
));
logger.info("Successfully logged into YouTube Music for account: {}", accountDetails.email());
loggedInSuccessfully = true;
cookieManager.saveCookiesToJson(driver, accountDetails.email());
} catch (TimeoutException e) {
logger.warn("Could not confirm login on YouTube Music. Account icon not found. Current URL: {}", driver.getCurrentUrl());
loggedInSuccessfully = true;
cookieManager.saveCookiesToJson(driver, accountDetails.email());
}
} else if (currentUrl.contains("myaccount.google.com") || currentUrl.contains("accounts.google.com")) {
logger.info("Redirected to a Google account page, attempting to navigate to YouTube Music again to confirm login.");
driver.get("https://music.youtube.com/");
sleep(3000);
try {
wait.until(ExpectedConditions.visibilityOfElementLocated(
By.xpath("//ytmusic-popup-container//ytmusic-settings-button | //a[@aria-label='Account'] | //yt-img-shadow[@class='style-scope ytmusic-nav-bar no-transition' and @height='32']")
));
logger.info("Successfully confirmed login on YouTube Music after redirect: {}", accountDetails.email());
loggedInSuccessfully = true;
cookieManager.saveCookiesToJson(driver, accountDetails.email());
} catch (TimeoutException e) {
logger.warn("Could not confirm login on YouTube Music after redirect. Account icon not found. Current URL: {}", driver.getCurrentUrl());
loggedInSuccessfully = true;
cookieManager.saveCookiesToJson(driver, accountDetails.email());
}
} else if (driver.findElements(By.xpath("//*[contains(text(),'Verify it\'s you') or contains(text(),'Подтвердите, что это вы')]")).size() > 0 ||
driver.findElements(By.xpath("//*[contains(text(),'recovery email') or contains(text(),'резервный адрес электронной почты')]")).size() > 0) {
logger.warn("Google requires verification for account {}. This scenario is not handled by automated login.", accountDetails.email());
} else {
logger.warn("Login may have failed for account: {}. Not on YouTube Music and no clear verification page. Current URL: {}", accountDetails.email(), currentUrl);
loggedInSuccessfully = true;
cookieManager.saveCookiesToJson(driver, accountDetails.email());
}
if (loggedInSuccessfully) {
boolean playClicked = musicHandler.playFirstTrackOrVideo();
if (playClicked) {
logger.info("Клик по первому видео/треку выполнен успешно после логина.");
} else {
logger.warn("Не удалось кликнуть по первому видео/треку после логина.");
}
accountManager.markAccountAsUsed(accountDetails);
logger.info("Account {} processed successfully. Proceeding to gather POT token and cookies.", accountDetails.email());
String potToken = musicHandler.getPotToken();
if (potToken != null && !potToken.isEmpty()) {
logger.info("POT Token for {}: {}", accountDetails.email(), potToken);
} else {
logger.warn("Failed to retrieve POT token for account: {}", accountDetails.email());
}
String cookiesNetscape = cookieManager.exportCookiesToNetscape(driver);
if (cookiesNetscape != null && !cookiesNetscape.isEmpty()) {
logger.info("Cookies (Netscape format) for {}: \n{}", accountDetails.email(), cookiesNetscape);
} else {
logger.warn("Failed to export cookies for account: {}", accountDetails.email());
}
logger.info("Waiting for 10 seconds to observe the page (demonstration)...");
sleep(10000);
} else {
logger.error("Login failed or unconfirmed for account {}. Marking as potentially problematic but not used.", accountDetails.email());
}
} catch (NoSuchWindowException e) {
logger.error("Browser window was closed unexpectedly during processing for account {}. Proxy: {}. Error: {}", accountDetails.email(), chosenProxy, e.getMessage());
makeGlobalScreenshot(driver, "error_window_closed_" + accountDetails.email().split("@")[0]);
} catch (TimeoutException e) {
logger.error("TimeoutException during processing for account {}. Proxy: {}. Current URL: {}. Error: {}", accountDetails.email(), chosenProxy, driver != null ? driver.getCurrentUrl() : "N/A", e.getMessage());
makeGlobalScreenshot(driver, "error_timeout_" + accountDetails.email().split("@")[0]);
} catch (WebDriverException e) {
logger.error("WebDriverException during processing for account {}. Proxy: {}. Error: {}", accountDetails.email(), chosenProxy, e.getMessage(), e);
makeGlobalScreenshot(driver, "error_webdriver_" + accountDetails.email().split("@")[0]);
} catch (Exception e) {
logger.error("An unexpected error occurred during login or captcha solving for account {}. Proxy: {}. Error: {}", accountDetails.email(), chosenProxy, e.getMessage(), e);
makeGlobalScreenshot(driver, "error_unexpected_login_" + accountDetails.email().split("@")[0]);
} finally {
if (driver != null) {
try {
logger.info("Quitting WebDriver for account: {}", accountDetails.email());
driver.quit();
} catch (Exception e) {
logger.error("Error quitting WebDriver for account: {}: {}", accountDetails.email(), e.getMessage());
}
}
if (tempProfilePath != null) {
deleteTempProfileDirectory(tempProfilePath);
}
logger.info("Finished processing for account: {} with proxy: {}", accountDetails.email(), chosenProxy != null ? chosenProxy : "none");
chosenProxy = null;
}
}
private void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.warn("Sleep interrupted");
}
}
private void bindProxy6ToIp() {
if (appProperties.getProxy6() == null || appProperties.getProxy6().getApiKey() == null || appProperties.getProxy6().getApiKey().isEmpty()) {
logger.warn("Proxy6 API key is not configured. Skipping IP binding.");
return;
}
String apiKey = appProperties.getProxy6().getApiKey();
String ip = "95.26.162.41";
String urlString = String.format("https://px6.link/api/%s/ipauth?ip=%s", apiKey, ip);
try {
URL url = new URL(urlString);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setConnectTimeout(15000);
con.setReadTimeout(15000);
int status = con.getResponseCode();
BufferedReader in = new BufferedReader(
new InputStreamReader((status == 200) ? con.getInputStream() : con.getErrorStream(),
StandardCharsets.UTF_8));
String inputLine;
StringBuilder content = new StringBuilder();
while ((inputLine = in.readLine()) != null) {
content.append(inputLine);
}
in.close();
con.disconnect();
if (status != 200 || !content.toString().contains("\"status\":\"yes\"")) {
logger.warn("Ошибка привязки IP к proxy6: " + content);
} else {
logger.info("Прокси успешно привязаны к IP через proxy6.net!");
}
} catch (Exception e) {
logger.error("Ошибка при привязке proxy6 к IP", e);
}
}
private void makeGlobalScreenshot(WebDriver driver, String filename) {
if (driver instanceof TakesScreenshot) {
try {
File screenshotFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
Path destinationDir = Path.of("screenshots", "global");
Files.createDirectories(destinationDir);
Path screenshotPath = destinationDir.resolve(filename + ".png");
Files.copy(screenshotFile.toPath(), screenshotPath, java.nio.file.StandardCopyOption.REPLACE_EXISTING);
logger.info("Global screenshot taken: {}", screenshotPath);
} catch (IOException | WebDriverException ex) {
logger.error("Failed to take global screenshot: {}", ex.getMessage());
}
} else {
logger.warn("Driver does not implement TakesScreenshot, cannot take global screenshot.");
}
}
private void deleteTempProfileDirectory(Path profilePath) {
if (profilePath != null) {
try {
logger.info("Attempting to delete temporary profile directory: {}", profilePath);
try (java.util.stream.Stream<Path> walk = Files.walk(profilePath)) {
walk.sorted(java.util.Comparator.reverseOrder())
.map(Path::toFile)
.peek(f -> logger.debug("Deleting: {}", f.getAbsolutePath()))
.forEach(File::delete);
}
logger.info("Successfully deleted temporary profile directory: {}", profilePath);
} catch (IOException e) {
logger.error("Failed to delete temporary profile directory {}: {}", profilePath, e.getMessage());
}
}
}
private void randomSleep(int minMs, int maxMs) {
try {
Thread.sleep(minMs + new java.util.Random().nextInt(maxMs - minMs + 1));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
private void performIntensiveWarmup(WebDriver driver, WebDriverWait wait, String accountEmail) {
logger.info("Starting intensive warm-up for account: {}", accountEmail);
try {
// 1. Google Search & Interaction
if (!driver.getCurrentUrl().contains("google.com")) {
driver.get("https://www.google.com/");
randomSleep(1500, 2500);
}
// Принять куки Google, если есть
try {
WebElement acceptGoogleCookiesButton = new WebDriverWait(driver, Duration.ofSeconds(7))
.until(ExpectedConditions.elementToBeClickable(By.xpath("//button[.//span[normalize-space()='Принять все' or normalize-space()='Accept all']]")));
acceptGoogleCookiesButton.click();
logger.info("Accepted Google cookies consent (e.g., 'Принять все') during intensive warm-up.");
randomSleep(500, 1500);
} catch (TimeoutException e) {
logger.info("Google cookies consent button (e.g., 'Принять все') not found or not clickable during warm-up.");
}
String[] searchQueries = {"latest tech news", "popular music videos", "weather forecast", "best cooking recipes"};
String searchQuery = searchQueries[new java.util.Random().nextInt(searchQueries.length)];
WebElement searchBox = wait.until(ExpectedConditions.visibilityOfElementLocated(By.name("q")));
typeWithDelayActions(driver, searchBox, searchQuery, 80, 180);
searchBox.sendKeys(Keys.ENTER);
logger.info("Warm-up: Searched Google for '{}'", searchQuery);
randomSleep(2000, 4000);
for (int i = 0; i < 2; i++) { // 2 скролла
((JavascriptExecutor) driver).executeScript("window.scrollBy(0, arguments[0]);", 300 + new java.util.Random().nextInt(400));
randomSleep(1000, 2000);
}
// 2. Visit YouTube & Interact
driver.get("https://www.youtube.com/");
logger.info("Warm-up: Navigated to YouTube.com");
randomSleep(3000, 5000);
// Принять куки YouTube, если есть (могут отличаться от Google)
try {
WebElement acceptYouTubeCookiesButton = new WebDriverWait(driver, Duration.ofSeconds(7))
.until(ExpectedConditions.elementToBeClickable(
By.xpath("//button[.//yt-formatted-string[contains(text(),'Accept all') or contains(text(),'Принять все')]] | //tp-yt-paper-button[contains(@aria-label,'Accept') or contains(@aria-label,'Принять')]")
));
acceptYouTubeCookiesButton.click();
logger.info("Accepted YouTube cookies during intensive warm-up.");
randomSleep(1000, 2000);
} catch (TimeoutException e) {
logger.info("No YouTube cookies consent button found during warm-up.");
}
for (int i = 0; i < 2; i++) { // 2 скролла на YouTube
((JavascriptExecutor) driver).executeScript("window.scrollBy(0, arguments[0]);", 400 + new java.util.Random().nextInt(500));
randomSleep(1500, 2500);
}
logger.info("Warm-up: Scrolled on YouTube homepage.");
// Опционально: кликнуть на случайное видео (требует аккуратных селекторов)
// WebElement firstVideo = driver.findElement(By.cssSelector("ytd-rich-item-renderer #video-title-link"));
// if (firstVideo != null && firstVideo.isDisplayed()) { firstVideo.click(); randomSleep(5000, 8000); driver.navigate().back(); }
// 3. Visit another common site (e.g., Wikipedia - lightweight)
// driver.get("https://www.wikipedia.org/");
// logger.info("Warm-up: Navigated to Wikipedia.org");
// randomSleep(2000, 3000);
// ((JavascriptExecutor) driver).executeScript("window.scrollBy(0, arguments[0]);", 200 + new java.util.Random().nextInt(200));
// randomSleep(1000, 1500);
logger.info("Intensive warm-up finished for account: {}. Total duration approx ~1-2 mins.", accountEmail);
// Общая длительность этого прогрева будет зависеть от задержек. Можно добавить больше шагов для 3+ минут.
} catch (Exception e) {
logger.warn("Error during intensive warm-up for account {}: {}. Proceeding with login attempt.", accountEmail, e.getMessage());
makeGlobalScreenshot(driver, "intensive_warmup_error_" + accountEmail.replace("@", "_"));
}
}
// Вспомогательный метод для посимвольного ввода, если он нужен здесь
// Если такой уже есть в GoogleLoginHandler, лучше его переиспользовать или вынести в утилиты
private void typeWithDelayActions(WebDriver driver, WebElement element, String text, int minDelay, int maxDelay) {
org.openqa.selenium.interactions.Actions actions = new org.openqa.selenium.interactions.Actions(driver);
for (char c : text.toCharArray()) {
actions.sendKeys(element, String.valueOf(c)).perform();
randomSleep(minDelay, maxDelay);
}
}
}

View File

@ -13,7 +13,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
// import org.springframework.scheduling.annotation.Scheduled; // Закомментировано
import org.springframework.stereotype.Component;
import java.io.BufferedReader;
@ -62,7 +62,7 @@ public class Task
@Scheduled(fixedRate = 24 * 60 * 60 * 1000)
// @Scheduled(fixedRate = 24 * 60 * 60 * 1000) // Закомментировано
@Async
public void generateProfileMeta()
{

View File

@ -16,7 +16,6 @@ import org.openqa.selenium.By;
import org.openqa.selenium.PageLoadStrategy;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.logging.LogEntry;
import org.openqa.selenium.logging.LogType;
@ -29,6 +28,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.openqa.selenium.chrome.ChromeDriver;
import java.io.File;
import java.io.IOException;
@ -38,6 +38,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.*;
import java.util.logging.Level;
/**
* Реализация генератора аккаунтов Google
@ -438,105 +439,73 @@ public class GoogleAccountGeneratorImpl implements GoogleAccountGenerator
}
/**
* Настраивает и создает экземпляр ChromeDriver
* Настраивает и создает экземпляр обычного ChromeDriver
*
* @return настроенный веб-драйвер
*/
private WebDriver setupChromeDriver()
{
logger.info("Настройка и запуск ChromeDriver");
logger.info("Настройка и запуск обычного ChromeDriver");
try
{
// Явно вызываем WebDriverManager для установки/обновления ChromeDriver
logger.info("Вызов WebDriverManager.chromedriver().setup()...");
WebDriverManager.chromedriver().setup();
logger.info("WebDriverManager.chromedriver().setup() завершен.");
// Путь к ChromeDriver уже должен быть установлен в Main.java - оставляем комментарий, но полагаемся на WDM
// logger.info("Используем путь к ChromeDriver, установленный в Main.java");
ChromeOptions options = new ChromeOptions();
// Если Chrome установлен НЕ в стандартное место, раскомментируйте и укажите путь:
// options.setBinary("C:\\Путь\\К\\Вашему\\chrome.exe");
// Используем EAGER стратегию загрузки страницы, чтобы не ждать полной загрузки ресурсов
options.setPageLoadStrategy(PageLoadStrategy.EAGER);
// Добавляем User-Agent
options.addArguments(
"--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"); // Пример современного User-Agent
// Создаём уникальный временный профиль для Chrome
// Уникальный fingerprint
options.addArguments("--user-agent=" + com.google.accountgen.service.FingerprintUtils.getRandomUserAgent());
String windowSize = com.google.accountgen.service.FingerprintUtils.getRandomWindowSize();
options.addArguments("--window-size=" + windowSize);
options.addArguments("--lang=" + com.google.accountgen.service.FingerprintUtils.getRandomLang());
Path tempProfile = Files.createTempDirectory("chrome-profile-");
options.addArguments("--user-data-dir=" + tempProfile.toAbsolutePath());
logger.info("Используется временный профиль Chrome: {}", tempProfile);
// Настройка логирования браузера для захвата консольных логов
LoggingPreferences logPrefs = new LoggingPreferences();
logPrefs.enable(LogType.BROWSER, java.util.logging.Level.ALL); // Захват всех консольных логов
// Отключаем Performance лог, т.к. он был закомментирован
logPrefs.enable(LogType.PERFORMANCE, java.util.logging.Level.OFF);
logPrefs.enable(LogType.BROWSER, Level.ALL);
logPrefs.enable(LogType.PERFORMANCE, Level.OFF);
options.setCapability("goog:loggingPrefs", logPrefs);
logger.info("Настроено логирование браузера (консоль).");
// --- ОТКЛЮЧАЕМ HEADLESS ---
logger.info("Headless режим ВЫКЛЮЧЕН.");
// options.addArguments("--headless=new"); // Закомментировано
// options.addArguments("--disable-gpu"); // Закомментировано (обычно не нужен без headless)
// options.addArguments("--window-size=1920,1080"); // Размер окна можно установить ниже (или в других опциях)
// --- КОНЕЦ ОТКЛЮЧЕНИЯ HEADLESS ---
// Оставляем общие опции
options.addArguments("--no-sandbox"); // Может быть полезно и не в headless
options.addArguments("--disable-dev-shm-usage"); // Может быть полезно и не в headless
options.addArguments("--disable-extensions"); // Явно отключаем расширения
options.addArguments("--no-sandbox");
options.addArguments("--disable-dev-shm-usage");
options.addArguments("--disable-extensions");
options.addArguments("--disable-blink-features=AutomationControlled");
options.addArguments("--disable-notifications");
options.addArguments("--disable-popup-blocking");
options.addArguments("--log-level=3");
options.addArguments("--silent");
// --- ДОБАВЛЕННЫЕ ОПЦИИ ДЛЯ МАСКИРОВКИ ---
options.addArguments("--disable-infobars"); // Убрать инфо-панель (дополнительно к AutomationControlled)
options.addArguments("--window-size=1366,768"); // Более стандартный размер окна
options.addArguments("--lang=tr-TR,tr;q=0.9,en-US;q=0.8,en;q=0.7"); // Установить турецкий язык + запасные
options.setExperimentalOption("excludeSwitches", Collections.singletonList("enable-automation")); // Еще один способ скрыть автоматизацию
options.setExperimentalOption("useAutomationExtension", false); // Отключить расширение автоматизации
// --- КОНЕЦ ДОБАВЛЕННЫХ ОПЦИЙ ---
// Временно отключаем добавление расширения VPN
logger.info("Загрузка VPN расширения временно отключена.");
// Proxy через Selenium Proxy capability
options.addArguments("--disable-infobars");
if (useProxy && proxyManager.hasProxies())
{
chosenProxy = proxyManager.getNextProxy();
if (chosenProxy == null) {
logger.error("Не удалось получить ни одного подходящего прокси с proxy6.net. Проверьте фильтры IP/тип или наличие активных прокси.");
throw new RuntimeException("Нет доступных прокси для использования. Проверьте настройки proxy6.net и фильтры в ProxyManager.");
if (chosenProxy != null)
{
org.openqa.selenium.Proxy httpProxy = new org.openqa.selenium.Proxy();
httpProxy.setHttpProxy(chosenProxy).setSslProxy(chosenProxy);
options.setCapability(CapabilityType.PROXY, httpProxy);
logger.info("HTTP(S) proxy настроен: {}", chosenProxy);
}
else
{
logger.error("Не удалось получить прокси от ProxyManager, хотя прокси включены и должны быть доступны.");
}
// Используем только host:port без логина и пароля
String finalProxy = "--proxy-server=http://" + chosenProxy;
options.addArguments(finalProxy);
org.openqa.selenium.Proxy httpProxy = new org.openqa.selenium.Proxy();
httpProxy.setHttpProxy(chosenProxy).setSslProxy(chosenProxy);
options.setCapability(CapabilityType.PROXY, httpProxy);
logger.info("HTTP(S) proxy настроен: http://{}", chosenProxy);
}
// Создаем и возвращаем драйвер
logger.info("Попытка создания экземпляра ChromeDriver с опциями...");
else if (useProxy)
{
logger.warn("Прокси включены в конфигурации, но ProxyManager не имеет доступных прокси.");
}
logger.info("Создаём обычный ChromeDriver");
WebDriver driver = new ChromeDriver(options);
// --- Антидетект JS ---
org.openqa.selenium.JavascriptExecutor js = (org.openqa.selenium.JavascriptExecutor) driver;
js.executeScript("Object.defineProperty(navigator, 'webdriver', {get: () => undefined});");
js.executeScript("Object.defineProperty(navigator, 'languages', {get: () => ['en-US', 'en']});");
js.executeScript("Object.defineProperty(navigator, 'plugins', {get: () => [1,2,3,4,5]});");
js.executeScript("Intl.DateTimeFormat.prototype.resolvedOptions = function() { return { timeZone: '" + com.google.accountgen.service.FingerprintUtils.getRandomTimezone() + "' }; };");
// --- END ---
logger.info("Экземпляр ChromeDriver успешно создан.");
return driver;
}
catch (Exception e)
{
logger.error("Критическая ошибка при настройке или запуске ChromeDriver", e);
throw new RuntimeException("Не удалось инициализировать ChromeDriver", e); // Пробрасываем исключение дальше
throw new RuntimeException("Не удалось инициализировать ChromeDriver", e);
}
}

View File

@ -0,0 +1,85 @@
package com.google.accountgen.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties
@Data // Lombok dlya avtomaticheskoy generacii getterov, setterov, toString, etc.
public class AppProperties {
private DarkShoppingProps darkshopping = new DarkShoppingProps();
private AccountsProps accounts = new AccountsProps();
private ImapProps imap = new ImapProps();
private Proxy6Props proxy6 = new Proxy6Props();
private Cookies cookies = new Cookies();
// Существующие свойства из вашего application.yml (если нужны в виде бина)
private String smsActivateApiKey;
private String captchaApiKey;
private String captchaServiceType;
private int retryCount;
private boolean useProxy;
private String proxyLogin;
private String proxyPass;
private boolean useVpn;
private String vpnExtensionPath;
private boolean useGoodbyedpi;
private String goodbyedpiPath;
private String smsService;
private int accountCount;
private long delayBetweenAttempts;
private int defaultCountry;
@Data
public static class DarkShoppingProps {
private String apiKey;
private String productId;
private int purchaseQuantity;
}
@Data
public static class AccountsProps {
private String directory;
}
@Data
public static class ImapProps {
private String host;
private int port;
private String username;
private String password;
private boolean ssl;
}
@Data
public static class Proxy6Props {
private String apiKey;
}
public void setAccounts(AccountsProps accounts) {
this.accounts = accounts;
}
public Cookies getCookies() {
return cookies;
}
public void setCookies(Cookies cookies) {
this.cookies = cookies;
}
public static class Cookies {
private String directory = "accounts_cookies";
public String getDirectory() {
return directory;
}
public void setDirectory(String directory) {
this.directory = directory;
}
}
}

View File

@ -5,8 +5,19 @@ import org.openqa.selenium.WebDriver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Date; // Для преобразования expiry
import java.util.Objects;
import java.util.Set;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; // Для дат, если понадобится
/**
* Класс для управления cookies браузера
@ -14,6 +25,26 @@ import java.util.Set;
public class CookieManager
{
private static final Logger logger = LoggerFactory.getLogger(CookieManager.class);
private final ObjectMapper objectMapper;
private final String cookiesStorageDir; // Новое поле
public CookieManager(String cookiesStorageDir) {
this.cookiesStorageDir = cookiesStorageDir;
this.objectMapper = new ObjectMapper();
this.objectMapper.registerModule(new JavaTimeModule()); // На случай, если Selenium Cookie использует типы Java Time
this.objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); // Для более читаемых дат, если применимо
// Гарантируем, что директория существует
try {
Files.createDirectories(Paths.get(this.cookiesStorageDir));
} catch (IOException e) {
logger.error("Could not create cookies storage directory: {}", this.cookiesStorageDir, e);
}
}
public CookieManager() { // Конструктор по умолчанию для обратной совместимости, если используется без директории
this("cookies_storage"); // Директория по умолчанию
logger.warn("CookieManager initialized with default cookies_storage directory. Consider providing a configured path.");
}
/**
* Экспортирует cookies в формате Netscape
@ -58,4 +89,71 @@ public class CookieManager
cookie.getValue());
}
public void saveCookiesToJson(WebDriver driver, String accountIdentifier) {
Path cookieFile = Paths.get(cookiesStorageDir, accountIdentifier + "_cookies.json");
Set<Cookie> cookies = driver.manage().getCookies();
if (cookies.isEmpty()) {
logger.info("No cookies to save for {}", accountIdentifier);
return;
}
try {
Files.createDirectories(cookieFile.getParent()); // Убедимся, что директория существует
String jsonCookies = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(cookies);
Files.writeString(cookieFile, jsonCookies, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
logger.info("Successfully saved {} cookies for {} to {}", cookies.size(), accountIdentifier, cookieFile);
} catch (IOException e) {
logger.error("Failed to save cookies for {} to {}: {}", accountIdentifier, cookieFile, e.getMessage(), e);
}
}
public void loadCookiesFromJson(WebDriver driver, String accountIdentifier) {
Path cookieFile = Paths.get(cookiesStorageDir, accountIdentifier + "_cookies.json");
if (!Files.exists(cookieFile)) {
logger.info("No cookie file found for {} at {}. Skipping cookie loading.", accountIdentifier, cookieFile);
return;
}
try {
String jsonCookies = Files.readString(cookieFile);
Set<Cookie> cookies = objectMapper.readValue(jsonCookies, new TypeReference<Set<Cookie>>() {});
// Перед загрузкой cookies важно находиться на домене, для которого они предназначены,
// или на родительском домене. Selenium может не загрузить cookie, если домен не совпадает.
// Обычно безопасно перейти на главный домен Google перед загрузкой.
// driver.get("https://.google.com"); // или driver.get("https://youtube.com");
// Эта навигация должна быть сделана ДО вызова loadCookiesFromJson, в вызывающем коде.
int loadedCount = 0;
for (Cookie cookie : cookies) {
// Selenium Cookie класс не всегда хорошо десериализуется из чистого JSON,
// если есть специфичные для Selenium поля или конструкторы.
// Попробуем создать новый объект Cookie, чтобы убедиться в корректности полей.
// Особенно важен expiry date, который должен быть объектом Date, а не long.
Cookie.Builder cookieBuilder = new Cookie.Builder(cookie.getName(), cookie.getValue())
.domain(cookie.getDomain())
.path(cookie.getPath())
.isSecure(cookie.isSecure())
.isHttpOnly(cookie.isHttpOnly())
.sameSite(cookie.getSameSite());
if (cookie.getExpiry() != null) {
cookieBuilder.expiresOn(cookie.getExpiry());
}
try {
driver.manage().addCookie(cookieBuilder.build());
loadedCount++;
} catch (Exception e) {
logger.warn("Failed to load cookie: {} for domain {}. Error: {}. Skipping this cookie.",
cookie.getName(), cookie.getDomain(), e.getMessage());
}
}
logger.info("Successfully loaded {} cookies for {} from {}", loadedCount, accountIdentifier, cookieFile);
} catch (IOException e) {
logger.error("Failed to load cookies for {} from {}: {}", accountIdentifier, cookieFile, e.getMessage(), e);
} catch (Exception e) {
logger.error("Error parsing cookies for {} from {}: {}", accountIdentifier, cookieFile, e.getMessage(), e);
}
}
}

View File

@ -0,0 +1,388 @@
package com.google.accountgen.login;
import com.google.accountgen.service.AccountManager;
import com.google.accountgen.service.HumanEmulationUtils;
import com.google.accountgen.service.TwoCaptchaService;
import org.openqa.selenium.*;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.Duration;
import java.util.List;
import java.util.Random;
public class GoogleLoginHandler {
private static final Logger logger = LoggerFactory.getLogger(GoogleLoginHandler.class);
private final WebDriver driver;
private final WebDriverWait wait;
private final WebDriverWait shortWait;
private final Random random = new Random();
private final TwoCaptchaService twoCaptchaService;
public GoogleLoginHandler(WebDriver driver, WebDriverWait wait, TwoCaptchaService twoCaptchaService) {
this.driver = driver;
this.wait = wait;
this.shortWait = new WebDriverWait(driver, Duration.ofSeconds(7)); // Короткое ожидание для необязательных элементов
this.twoCaptchaService = twoCaptchaService;
}
private void warmUpOnYouTubeMusicPage() {
logger.info("Warming up on YouTube Music page...");
try {
// 1. Случайная прокрутка
int scrolls = random.nextInt(2) + 1; // 1 to 2 scrolls
for (int i = 0; i < scrolls; i++) {
randomScroll();
randomSleep(800, 1500);
}
// 2. Попытка найти и кликнуть на "Explore" или "Обзор" (если есть и не требует логина)
// Эти селекторы могут потребовать уточнения
try {
WebElement exploreLink = shortWait.until(ExpectedConditions.elementToBeClickable(
By.xpath("//ytmusic-pivot-bar-item-renderer[.//yt-formatted-string[contains(., 'Explore') or contains(., 'Обзор')]]/a | //a[contains(@href, '/explore')]")
));
if (exploreLink.isDisplayed() && exploreLink.isEnabled()) {
logger.info("Found 'Explore' link, clicking for warm-up.");
mouseMoveAndClick(exploreLink);
randomSleep(1500, 3000); // Даем время на возможную загрузку
// Вернемся назад, чтобы продолжить основной сценарий
driver.navigate().back();
logger.info("Navigated back after warm-up click.");
randomSleep(1000, 2000);
}
} catch (TimeoutException | NoSuchElementException e) {
logger.info("'Explore' link not found or not clickable for warm-up, skipping.");
}
// 3. Еще одна случайная прокрутка
randomScroll();
randomSleep(500, 1200);
} catch (Exception e) {
logger.warn("Error during warm-up on YouTube Music page, continuing...", e);
}
logger.info("Warm-up finished.");
}
public void navigateToYouTubeMusicAndHandlePopups() {
logger.info("Navigating to YouTube Music and handling initial popups...");
driver.get("https://music.youtube.com/");
makeScreenshot("yt_music_opened");
randomSleep(1000, 2500); // Дополнительная задержка после загрузки
warmUpOnYouTubeMusicPage(); // <--- Вызов "прогрева"
// 1. Окно согласия (часто появляется первым)
if (driver.getCurrentUrl().contains("consent.youtube.com") || driver.getCurrentUrl().contains("consent.google.com")) {
logger.info("Consent page detected.");
try {
// Обновленный и исправленный XPath
WebElement acceptAllButton = wait.until(ExpectedConditions.elementToBeClickable(
By.xpath("//button[.//span[normalize-space()='Принять все' or normalize-space()='Accept all']]")
));
acceptAllButton.click();
logger.info("Clicked 'Accept all' or similar on consent page.");
wait.until(ExpectedConditions.not(ExpectedConditions.urlContains("consent.google.com")));
wait.until(ExpectedConditions.not(ExpectedConditions.urlContains("consent.youtube.com")));
makeScreenshot("yt_consent_accepted");
sleep(2000);
} catch (TimeoutException e) {
logger.warn("Could not find or click 'Accept all' button on consent page, or did not leave consent page in time.");
makeScreenshot("yt_consent_error");
}
}
// 2. Другие возможные всплывающие окна YouTube Music (например, "Try Premium", "Get to know the new YouTube Music" и т.п.)
try {
WebElement noThanksButton = shortWait.until(ExpectedConditions.elementToBeClickable(
By.xpath("//ytmusic-popup-container//paper-button[contains(normalize-space(),'No thanks')] | //ytmusic-popup-container//yt-button-renderer[contains(normalize-space(),'No thanks') or @aria-label='Dismiss'] | //button[contains(normalize-space(),'No Thanks')]")));
if (noThanksButton.isDisplayed() && noThanksButton.isEnabled()) {
logger.info("Found a 'No thanks' or 'Dismiss' button in a popup. Clicking...");
mouseMoveAndClick(noThanksButton);
makeScreenshot("yt_popup_dismissed");
randomSleep(1500, 2500);
}
} catch (TimeoutException e) {
logger.info("No 'No thanks' or 'Dismiss' popup found or it's not clickable.");
}
logger.info("Finished handling initial YouTube popups.");
}
public void clickSignInOnYouTubeMusic() {
logger.info("Attempting to click Sign In button on YouTube Music...");
WebElement signInButton = wait.until(ExpectedConditions.elementToBeClickable(
By.cssSelector("a.sign-in-link.app-bar-button.style-scope.ytmusic-nav-bar")));
mouseMoveAndClick(signInButton);
makeScreenshot("yt_signin_clicked");
logger.info("Sign In button clicked.");
randomSleep(1500, 3000); // Увеличена пауза для загрузки страницы логина Google
}
public void performGoogleLogin(AccountManager.AccountDetails accountDetails) throws Exception {
logger.info("Performing Google login for account: {}", accountDetails.email());
// Ввод логина
logger.info("Entering email: {}", accountDetails.email());
WebElement emailInput = wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("input[type='email']#identifierId")));
typeWithDelay(emailInput, accountDetails.email());
randomSleep(300, 800); // Небольшая пауза перед кликом "Далее"
makeScreenshot("login_email_entered");
WebElement nextBtnEmail = driver.findElement(By.cssSelector("#identifierNext button"));
mouseMoveAndClick(nextBtnEmail);
logger.info("Email submitted.");
randomSleep(1800, 3200); // Увеличена пауза для возможного появления капчи или загрузки след. шага
// Проверка на reCAPTCHA
try {
logger.debug("Checking for reCAPTCHA iframe...");
// Ожидаем iframe с reCAPTCHA (стандартные селекторы)
WebElement recaptchaFrame = shortWait.until(ExpectedConditions.presenceOfElementLocated(
By.xpath("//iframe[starts-with(@name, 'a-') and starts-with(@src, 'https://www.google.com/recaptcha/api2/anchor?')] | //iframe[@title='reCAPTCHA']")
));
if (recaptchaFrame != null && recaptchaFrame.isDisplayed()) {
logger.info("reCAPTCHA iframe detected. Attempting to interact...");
makeScreenshot("recaptcha_iframe_detected");
randomSleep(500,1200); // Пауза перед переключением во фрейм
// 1. Switch to reCAPTCHA iframe
driver.switchTo().frame(recaptchaFrame);
logger.debug("Switched to reCAPTCHA iframe.");
// 2. Attempt to click the checkbox
boolean checkboxClicked = false;
try {
WebElement recaptchaCheckbox = new WebDriverWait(driver, Duration.ofSeconds(7)) // Используем явное ожидание для чекбокса
.until(ExpectedConditions.elementToBeClickable(By.id("recaptcha-anchor")));
if (recaptchaCheckbox.isDisplayed() && recaptchaCheckbox.isEnabled()) {
logger.info("reCAPTCHA checkbox (id: recaptcha-anchor) found. Clicking...");
mouseMoveAndClick(recaptchaCheckbox);
randomSleep(1200, 2200); // Увеличена пауза после клика
makeScreenshot("recaptcha_checkbox_clicked");
logger.info("reCAPTCHA checkbox (id: recaptcha-anchor) clicked successfully.");
checkboxClicked = true;
} else {
logger.warn("reCAPTCHA checkbox (id: recaptcha-anchor) found but not displayed or enabled.");
makeScreenshot("recaptcha_checkbox_not_visible_or_enabled");
}
} catch (TimeoutException | NoSuchElementException e) {
logger.warn("reCAPTCHA checkbox (id: recaptcha-anchor) not found or not clickable within its iframe. It might appear later or not at all.");
makeScreenshot("recaptcha_checkbox_not_found_or_unclickable");
}
// 3. Switch back to default content to find sitekey elements
driver.switchTo().defaultContent();
logger.debug("Switched back to default content to find sitekey.");
randomSleep(300, 700); // Пауза после возврата из фрейма
String siteKey = null;
// 4. Try to get sitekey from data-sitekey attribute of a g-recaptcha div
try {
List<WebElement> siteKeyElements = driver.findElements(By.className("g-recaptcha"));
if (!siteKeyElements.isEmpty()) {
logger.debug("Found {} elements with class 'g-recaptcha'. Checking for data-sitekey...", siteKeyElements.size());
for (WebElement el : siteKeyElements) {
if (el.isDisplayed()) { // Check only visible elements
siteKey = el.getAttribute("data-sitekey");
if (siteKey != null && !siteKey.isEmpty()) {
logger.info("Found sitekey '{}' in a visible 'g-recaptcha' div's data-sitekey attribute.", siteKey);
break;
}
}
}
if (siteKey == null || siteKey.isEmpty()) {
logger.warn("Found 'g-recaptcha' div(s), but data-sitekey attribute is missing, empty, or on a non-displayed element.");
}
} else {
logger.warn("Could not find any div with class 'g-recaptcha' in default content.");
}
} catch (Exception e) {
logger.warn("Exception while trying to find sitekey in 'g-recaptcha' div's data-sitekey attribute.", e);
}
// 5. If sitekey not found from div, try to get it from the iframe's src URL (k= parameter)
// We need to find the recaptchaFrame element again because we switched to defaultContent.
if (siteKey == null || siteKey.isEmpty()) {
logger.info("Sitekey not found in data-sitekey attribute. Attempting to extract from iframe src.");
try {
// Re-locate the iframe; its reference might be stale
WebElement iframeForSrc = new WebDriverWait(driver, Duration.ofSeconds(5))
.until(ExpectedConditions.presenceOfElementLocated(
By.xpath("//iframe[starts-with(@name, 'a-') and starts-with(@src, 'https://www.google.com/recaptcha/api2/anchor?')] | //iframe[@title='reCAPTCHA']")
));
String iframeSrc = iframeForSrc.getAttribute("src");
if (iframeSrc != null && iframeSrc.contains("k=")) {
siteKey = iframeSrc.split("k=")[1].split("&")[0];
logger.info("Extracted sitekey '{}' from iframe src.", siteKey);
} else {
logger.warn("Could not extract sitekey from iframe src (k= parameter not found or src is null). Iframe src: {}", iframeSrc);
makeScreenshot("recaptcha_sitekey_k_param_not_found");
}
} catch (Exception e) {
logger.warn("Exception while trying to re-locate iframe and extract sitekey from its src.", e);
makeScreenshot("recaptcha_sitekey_iframe_src_exception");
}
}
// At this point, we are in defaultContent.
if (siteKey == null || siteKey.isEmpty()) {
logger.error("Failed to find or extract reCAPTCHA sitekey. Cannot proceed with 2Captcha.");
makeScreenshot("recaptcha_sitekey_extraction_failed_overall");
throw new RuntimeException("reCAPTCHA sitekey not found. Login aborted for: " + accountDetails.email());
}
logger.info("Using reCAPTCHA sitekey: {}. Checkbox clicked: {}", siteKey, checkboxClicked);
// Call 2Captcha (we are already in defaultContent)
String captchaToken = twoCaptchaService.solveRecaptchaV2(siteKey, driver.getCurrentUrl());
if (captchaToken != null && !captchaToken.isEmpty()) {
logger.info("reCAPTCHA solved by 2Captcha. Token: {}", captchaToken);
WebElement captchaResponseElement = driver.findElement(By.id("g-recaptcha-response")); // Should be in defaultContent
((JavascriptExecutor) driver).executeScript(
"arguments[0].style.display = 'block'; arguments[0].value = arguments[1];",
captchaResponseElement,
captchaToken
);
((JavascriptExecutor) driver).executeScript(
"var evt = new Event('input', { bubbles: true, cancelable: false }); arguments[0].dispatchEvent(evt);",
captchaResponseElement
);
((JavascriptExecutor) driver).executeScript(
"var evt = new Event('change', { bubbles: true, cancelable: false }); arguments[0].dispatchEvent(evt);",
captchaResponseElement
);
makeScreenshot("recaptcha_token_injected_events_fired_default_content");
randomSleep(1200, 2200); // Увеличена пауза
String nextButtonXPath = "//button[.//span[contains(text(),'Далее') or contains(text(),'Next')]]"; // Should be in defaultContent
WebElement nextBtnAfterCaptcha = wait.until(ExpectedConditions.elementToBeClickable(By.xpath(nextButtonXPath)));
logger.info("Attempting to click 'Next' button (after 2captcha token) with XPath: {}", nextButtonXPath);
mouseMoveAndClick(nextBtnAfterCaptcha);
logger.info("Clicked 'Next' button after 2captcha token submission.");
randomSleep(2500, 4000); // Увеличена пауза
} else {
logger.error("Failed to solve reCAPTCHA with 2Captcha for {}. Token was null or empty.", accountDetails.email());
makeScreenshot("recaptcha_2captcha_solve_failed");
throw new RuntimeException("Failed to solve reCAPTCHA via 2Captcha. Login aborted for: " + accountDetails.email());
}
} // End of: if (recaptchaFrame != null && recaptchaFrame.isDisplayed())
} catch (TimeoutException e) {
logger.info("No reCAPTCHA iframe detected within the timeout or it's not displayed. Proceeding without captcha solving.");
makeScreenshot("no_recaptcha_iframe_initial_check");
} catch (NoSuchElementException e) { // Catching this at a higher level if the initial iframe isn't found
logger.info("reCAPTCHA iframe element not found. Proceeding without captcha solving.");
makeScreenshot("recaptcha_iframe_not_found_initial_check");
}
// Скроллим страницу для человечности
randomScroll();
// Ввод пароля
logger.info("Entering password...");
WebElement passwordInput = wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("input[type='password'][name='Passwd'], input[type='password'][name='password']")));
typeWithDelay(passwordInput, accountDetails.password());
randomSleep(300, 800); // Небольшая пауза перед кликом "Далее"
makeScreenshot("login_password_entered");
WebElement nextPassBtn = driver.findElement(By.cssSelector("#passwordNext button"));
mouseMoveAndClick(nextPassBtn);
logger.info("Password submitted.");
randomSleep(1200, 2500);
}
// Имитация ручного ввода текста с задержкой между символами
private void typeWithDelay(WebElement element, String text) {
Actions actions = new Actions(driver);
for (char c : text.toCharArray()) {
actions.sendKeys(element, String.valueOf(c)).perform();
randomSleep(90, 220); // Слегка увеличен диапазон для большей вариативности
}
}
// Имитация движения мыши и клик
private void mouseMoveAndClick(WebElement element) {
try {
Actions actions = new Actions(driver);
actions.moveToElement(element).pause(Duration.ofMillis(200 + random.nextInt(400))).click().perform();
randomSleep(150, 400);
} catch (Exception e) {
logger.warn("Mouse move/click imitation failed, fallback to direct click: {}", e.getMessage());
element.click();
}
}
// Рандомная задержка
private void randomSleep(int minMs, int maxMs) {
try {
Thread.sleep(minMs + random.nextInt(maxMs - minMs + 1));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// Имитация скроллинга страницы
private void randomScroll() {
JavascriptExecutor js = (JavascriptExecutor) driver;
long pageHeight = (Long) js.executeScript("return Math.max( document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight );");
int scrollAmount = random.nextInt((int) (pageHeight / 3)) + (int) (pageHeight / 5) ; // Прокрутка на случайную величину от 1/5 до ~1/2 высоты
String scrollScript = String.format("window.scrollBy(0, %d);", scrollAmount * (random.nextBoolean() ? 1 : -1)); // Случайное направление
if (pageHeight > 0) { // Только если есть что скроллить
js.executeScript(scrollScript);
logger.debug("Scrolled by a random amount.");
} else {
logger.debug("Page height is 0, skipping scroll.");
}
}
public void handlePostLoginPopups() {
logger.info("Handling post-login popups (e.g., 'Not now')...");
// Пример обработки кнопки "Not now" (адаптировано из GoogleRegistrationHandler)
try {
WebElement notNowButton = shortWait.until(ExpectedConditions.elementToBeClickable(
By.xpath("//button[.//span[text()='Not now' or text()='Не сейчас']]"))); // Добавил 'Не сейчас'
logger.info("Found 'Not now' button. Clicking...");
notNowButton.click();
makeScreenshot("login_not_now_clicked");
sleep(2000);
} catch (TimeoutException e) {
logger.info("'Not now' button not found or not clickable within the short wait time.");
makeScreenshot("login_not_now_not_found");
}
// Сюда можно добавить обработку других специфичных всплывающих окон после логина
}
// Вспомогательные методы (можно вынести в утилитарный класс, если их много или они общие)
private void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.warn("Sleep interrupted", e);
}
}
private void makeScreenshot(String name) {
try {
File screenshotFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
Path destinationDir = Path.of("screenshots");
Files.createDirectories(destinationDir);
String timestamp = new java.text.SimpleDateFormat("yyyyMMdd_HHmmssSSS").format(new java.util.Date());
Path destinationPath = destinationDir.resolve(name + "_" + timestamp + ".png");
Files.copy(screenshotFile.toPath(), destinationPath, StandardCopyOption.REPLACE_EXISTING);
logger.debug("Screenshot saved: {}", destinationPath);
} catch (IOException | WebDriverException e) {
logger.error("Failed to take or save screenshot '{}': {}", name, e.getMessage());
}
}
}

View File

@ -2,8 +2,10 @@ package com.google.accountgen.proxy;
import com.fasterxml.jackson.databind.MappingIterator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.accountgen.config.AppProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@ -24,35 +26,69 @@ public class ProxyManager
private static final Logger logger = LoggerFactory.getLogger(ProxyManager.class);
private static final String API_URL = "https://px6.link/api/cecd79b327-8f5b6b51aa-63cc268d0a/getproxy?state=active&limit=1000";
private final List<String> proxyList = new ArrayList<>();
private final Random random = new Random();
private final String proxy6ApiKey;
@Value("${proxyLogin}")
private String proxyLogin;
@Value("${proxyPass}")
private String proxypass;
@Autowired
public ProxyManager(AppProperties appProperties) {
if (appProperties.getProxy6() != null && appProperties.getProxy6().getApiKey() != null && !appProperties.getProxy6().getApiKey().isEmpty()) {
this.proxy6ApiKey = appProperties.getProxy6().getApiKey();
logger.info("ProxyManager initialized with proxy6 API key.");
} else {
this.proxy6ApiKey = null;
logger.warn("ProxyManager: proxy6 API key is not configured in AppProperties. Proxy fetching might fail if it relies on this key.");
}
}
public void init()
{
if (this.proxy6ApiKey == null) {
logger.error("Cannot initialize ProxyManager: proxy6 API key is null. Please configure proxy6.api_key in application.yml");
throw new RuntimeException("Proxy6 API key is not configured for ProxyManager.");
}
String apiUrl = String.format("https://px6.link/api/%s/getproxy?state=active&limit=1000", this.proxy6ApiKey);
logger.info("ProxyManager: Attempting to fetch proxies from URL: {}", apiUrl);
try
{
String response = sendRequest(API_URL);
String response = sendRequest(apiUrl);
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> root = mapper.readValue(response, Map.class);
if (!"yes".equals(root.get("status"))) {
logger.error("Failed to fetch proxies from proxy6. API response: {}", response);
throw new RuntimeException("Ошибка получения прокси: " + root.get("error"));
}
Map<String, Map<String, Object>> proxies = (Map<String, Map<String, Object>>) root.get("list");
if (proxies == null || proxies.isEmpty()) {
logger.warn("ProxyManager: No proxies found in the API response from proxy6 (list is null or empty).");
proxyList.clear();
return;
}
proxyList.clear();
for (Map<String, Object> proxy : proxies.values()) {
String host = (String) proxy.get("host");
String port = (String) proxy.get("port");
for (Map.Entry<String, Map<String, Object>> entry : proxies.entrySet()) {
Map<String, Object> proxyDetails = entry.getValue();
String host = (String) proxyDetails.get("host");
String port = (String) proxyDetails.get("port");
if (host != null && port != null) {
proxyList.add(host + ":" + port);
}
}
if (!proxyList.isEmpty()) {
logger.info("ProxyManager initialized successfully. Loaded {} proxies.", proxyList.size());
} else {
logger.warn("ProxyManager initialized, but no valid proxy entries (host:port) were parsed from the API response.");
}
} catch (Exception e) {
throw new RuntimeException(e);
logger.error("Failed to initialize ProxyManager while fetching or parsing proxies.", e);
throw new RuntimeException("Ошибка при инициализации ProxyManager: " + e.getMessage(), e);
}
}
@ -71,6 +107,7 @@ public class ProxyManager
public String getNextProxy()
{
if (proxyList.isEmpty()) {
logger.warn("getNextProxy called, but proxy list is empty.");
return null;
}
return proxyList.get(random.nextInt(proxyList.size()));
@ -79,28 +116,30 @@ public class ProxyManager
private String sendRequest(String urlString) throws Exception
{
URL url = new URL(urlString.replace(" ", "%20")); // Кодируем пробелы на всякий случай
URL url = new URL(urlString.replace(" ", "%20"));
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setConnectTimeout(15000); // Таймаут соединения 15 сек
con.setReadTimeout(15000); // Таймаут чтения 15 сек
con.setConnectTimeout(15000);
con.setReadTimeout(15000);
int status = con.getResponseCode();
BufferedReader in = new BufferedReader(
new InputStreamReader((status == 200) ? con.getInputStream() : con.getErrorStream(),
StandardCharsets.UTF_8));
String inputLine;
StringBuilder content = new StringBuilder();
while ((inputLine = in.readLine()) != null)
{
content.append(inputLine);
try (BufferedReader in = new BufferedReader(
new InputStreamReader((status >= 200 && status < 300) ? con.getInputStream() : con.getErrorStream(),
StandardCharsets.UTF_8))) {
String inputLine;
while ((inputLine = in.readLine()) != null)
{
content.append(inputLine);
}
} finally {
con.disconnect();
}
in.close();
con.disconnect();
if (status != 200)
if (status < 200 || status >= 300)
{
logger.error("Ошибка HTTP запроса к API: статус {}, Ответ: {}", status, content);
logger.error("Ошибка HTTP запроса к API: статус {}, URL: {}, Ответ: {}", status, urlString, content);
}
return content.toString();

View File

@ -17,6 +17,8 @@ import java.util.List;
import java.util.Objects;
import java.util.Random;
import com.google.accountgen.service.HumanEmulationUtils;
/**
* Класс обработчик для регистрации аккаунта Google
*/
@ -908,7 +910,7 @@ public class GoogleRegistrationHandler
// Очищаем поле перед вводом, на всякий случай
phoneInput.clear();
sleep(100);
phoneInput.sendKeys(phoneNumber); // Вводим номер без кода страны
HumanEmulationUtils.typeLikeHuman(phoneInput, phoneNumber); // Вводим номер без кода страны
sleep(300);
// 4. Найти и нажать кнопку "Next"
@ -1291,31 +1293,9 @@ public class GoogleRegistrationHandler
*/
private void typeWithRandomDelay(WebElement element, String text)
{
if (element == null || text == null)
{
return;
}
// Очищаем поле
if (element == null || text == null) return;
element.clear();
// Вводим символы с задержками
for (char c : text.toCharArray())
{
element.sendKeys(String.valueOf(c));
// Случайная задержка от 50 до 150 мс
try
{
Thread.sleep(50 + random.nextInt(100));
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
logger.warn("Прерывание потока при имитации ввода с задержкой", e);
break;
}
}
HumanEmulationUtils.typeLikeHuman(element, text);
}
/**

View File

@ -0,0 +1,160 @@
package com.google.accountgen.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
public class AccountManager {
private static final Logger logger = LoggerFactory.getLogger(AccountManager.class);
private final String accountsDir;
private final DarkShoppingService darkShoppingService;
private final String googleAccountProductId;
private final int purchaseQuantity;
private static final String USED_MARKER = " - использован";
public AccountManager(String accountsDir, DarkShoppingService darkShoppingService, String googleAccountProductId, int purchaseQuantity) {
this.accountsDir = accountsDir;
this.darkShoppingService = darkShoppingService;
this.googleAccountProductId = googleAccountProductId;
this.purchaseQuantity = purchaseQuantity; // Минимальное количество для нового продавца - 15
try {
Files.createDirectories(Paths.get(accountsDir));
} catch (IOException e) {
logger.error("Could not create accounts directory: {}", accountsDir, e);
}
}
private Optional<Path> findAvailableAccountFile() {
try (Stream<Path> stream = Files.list(Paths.get(accountsDir))) {
return stream
.filter(file -> !Files.isDirectory(file) && file.toString().endsWith(".txt"))
.filter(file -> {
try {
List<String> lines = Files.readAllLines(file, StandardCharsets.UTF_8);
for (String line : lines) {
if (!line.trim().isEmpty() && !line.trim().endsWith(USED_MARKER)) {
return true; // Найден неиспользованный аккаунт
}
}
return false; // Нет неиспользованных аккаунтов
} catch (IOException e) {
logger.error("Error reading file to check for unused accounts: {}", file, e);
return false;
}
})
.findFirst();
} catch (IOException e) {
logger.error("Error listing files in accounts directory: {}", accountsDir, e);
return Optional.empty();
}
}
public Optional<AccountDetails> getNextAccount() {
Optional<Path> accountFileOpt = findAvailableAccountFile();
while (true) {
if (accountFileOpt.isPresent()) {
Path accountFile = accountFileOpt.get();
try {
List<String> lines = Files.readAllLines(accountFile, StandardCharsets.UTF_8);
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
if (!line.trim().endsWith(USED_MARKER) && !line.trim().isEmpty()) {
// Поддержка форматов: email:password, email;password, email:password:add_email
String[] parts;
if (line.contains(";")) {
parts = line.split(";", 2);
} else {
parts = line.split(":");
}
if (parts.length == 2) {
String email = parts[0].trim();
String password = parts[1].trim().replace(USED_MARKER, "");
return Optional.of(new AccountDetails(email, password, null, null, accountFile, i));
} else if (parts.length == 3) {
String email = parts[0].trim();
String password = parts[1].trim();
String backupEmail = parts[2].trim().replace(USED_MARKER, "");
return Optional.of(new AccountDetails(email, password, backupEmail, null, accountFile, i));
} else if (parts.length == 4) {
String email = parts[0].trim();
String password = parts[1].trim();
String backupEmail = parts[2].trim();
String backupPassword = parts[3].trim().replace(USED_MARKER, "");
return Optional.of(new AccountDetails(email, password, backupEmail, backupPassword, accountFile, i));
} else {
logger.warn("Invalid account line format in {}: {}. Expected 2, 3, or 4 parts separated by colons.", accountFile, line);
}
}
}
// Все аккаунты в файле использованы или не найдены
logger.info("All accounts in file {} are used or file is invalid/empty. Deleting file.", accountFile);
Files.deleteIfExists(accountFile);
accountFileOpt = findAvailableAccountFile();
} catch (IOException e) {
logger.error("Error processing account file: {}", accountFile, e);
return Optional.empty();
}
} else {
logger.info("No available account files found. Attempting to purchase new accounts (Product ID: {}, Quantity: {}).", googleAccountProductId, purchaseQuantity);
boolean purchased = darkShoppingService.purchaseAccounts(googleAccountProductId, purchaseQuantity);
if (purchased) {
logger.info("Successfully purchased new accounts. Trying to get an account again.");
accountFileOpt = findAvailableAccountFile();
if (accountFileOpt.isEmpty()) {
logger.warn("Purchased accounts, but no new account file found immediately. There might be a delay or an issue.");
return Optional.empty();
}
} else {
logger.error("Failed to purchase new accounts.");
return Optional.empty();
}
}
}
}
public void markAccountAsUsed(AccountDetails accountDetails) {
try {
List<String> lines = Files.readAllLines(accountDetails.filePath(), StandardCharsets.UTF_8);
if (accountDetails.lineNumber() < lines.size()) {
String line = lines.get(accountDetails.lineNumber());
if (!line.trim().endsWith(USED_MARKER)) {
lines.set(accountDetails.lineNumber(), line + USED_MARKER);
Files.write(accountDetails.filePath(), lines, StandardCharsets.UTF_8);
logger.info("Marked account {} as used in file {}", accountDetails.email(), accountDetails.filePath());
} else {
logger.warn("Account {} was already marked as used in file {}", accountDetails.email(), accountDetails.filePath());
}
} else {
logger.error("Line number {} is out of bounds for file {}. Cannot mark account as used.", accountDetails.lineNumber(), accountDetails.filePath());
}
// Проверяем, все ли аккаунты в файле использованы
boolean allEffectivelyUsed = true;
for (String currentLine : Files.readAllLines(accountDetails.filePath(), StandardCharsets.UTF_8)) {
if (!currentLine.trim().isEmpty() && !currentLine.trim().endsWith(USED_MARKER)) {
allEffectivelyUsed = false;
break;
}
}
if (allEffectivelyUsed) {
logger.info("All accounts in {} are effectively used (or file is empty). Deleting file.", accountDetails.filePath());
Files.deleteIfExists(accountDetails.filePath());
}
} catch (IOException e) {
logger.error("Error marking account {} as used in file {}", accountDetails.email(), accountDetails.filePath(), e);
}
}
public record AccountDetails(String email, String password, String backupEmail, String backupPassword, Path filePath, int lineNumber) {}
}

View File

@ -0,0 +1,273 @@
package com.google.accountgen.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class DarkShoppingService {
private static final Logger logger = LoggerFactory.getLogger(DarkShoppingService.class);
private static final String API_BASE_URL = "https://dark.shopping/api/v1"; // Изменено
private final String apiKey;
private final OkHttpClient httpClient;
private final ObjectMapper objectMapper;
private final String accountsDir;
public DarkShoppingService(String apiKey, String accountsDir) {
this.apiKey = apiKey;
this.httpClient = new OkHttpClient();
this.objectMapper = new ObjectMapper();
this.accountsDir = accountsDir;
// Создаем директорию, если ее нет
try {
Files.createDirectories(Paths.get(accountsDir));
} catch (IOException e) {
logger.error("Could not create accounts directory: {}", accountsDir, e);
// Можно выбросить кастомное исключение или обработать иначе
}
}
// Метод для получения product_id (если потребуется)
public Optional<String> findGoogleAccountProductId(String groupId) {
Request request = new Request.Builder()
.url(API_BASE_URL + "/groups/" + groupId + "/products")
.header("Authorization", "Bearer " + apiKey)
.header("Accept", "application/json")
.build();
try (Response response = httpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
logger.error("Failed to get products for group {}: {} - {}", groupId, response.code(), response.body() != null ? response.body().string() : "Empty body");
return Optional.empty();
}
String responseBody = response.body().string();
logger.debug("Products response: {}", responseBody);
// Здесь нужно будет распарсить JSON и найти нужный product_id
// Примерная структура ответа:
// { "success": true, "data": [ { "id": "...", "name": "Google Account...", ... }, ... ] }
// Вам нужно будет найти товар с подходящим именем, например, содержащим "Google"
// и вернуть его "id". Этот код нужно будет дописать после получения реального ответа API.
// Пока возвращаем Optional.empty()
return Optional.empty(); // ЗАГЛУШКА
} catch (IOException e) {
logger.error("Error fetching products for group {}", groupId, e);
return Optional.empty();
}
}
public boolean purchaseAccounts(String productId, int quantity) {
logger.info("Attempting to purchase {} accounts for product ID: {}", quantity, productId);
RequestBody formBody = new FormBody.Builder()
.add("key", apiKey) // Добавлен apiKey как POST параметр
.add("product", productId) // Изменено с product_id на product
.add("quantity", String.valueOf(quantity))
// .add("email", "your_email_for_notifications@example.com") // Опционально, в документации это send_email_copy (boolean)
.build();
Request request = new Request.Builder()
.url(API_BASE_URL + "/order/create") // Изменен URL
// .header("Authorization", "Bearer " + apiKey) // Удалена авторизация через заголовок
.header("Accept", "application/json")
.post(formBody)
.build();
// Добавим логирование URL и тела запроса
logger.info("Sending POST request to: {}", request.url());
if (request.body() instanceof FormBody) {
FormBody actualFormBody = (FormBody) request.body();
StringBuilder formBodyContent = new StringBuilder();
for (int i = 0; i < actualFormBody.size(); i++) {
formBodyContent.append(actualFormBody.encodedName(i))
.append("=")
.append(actualFormBody.encodedValue(i))
.append(i < actualFormBody.size() - 1 ? "&" : "");
}
logger.info("Request body: {}", formBodyContent.toString());
}
try (Response response = httpClient.newCall(request).execute()) {
// Сохраняем тело ответа, чтобы прочитать его один раз
ResponseBody responseBodyObj = response.body();
String responseBodyString = (responseBodyObj != null) ? responseBodyObj.string() : null;
// Логируем HTTP статус и тело ответа ВСЕГДА
logger.info("Order creation response status: {}, body: {}", response.code(), responseBodyString);
if (!response.isSuccessful() || responseBodyString == null) {
// Это сообщение теперь будет более информативным из-за лога выше
logger.error("Failed to create order. HTTP status: {}, Body: {}", response.code(), responseBodyString);
return false;
}
// logger.debug("Order creation response: {}", responseBodyString); // Уже залогировано выше как INFO
Map<String, Object> orderResponse = objectMapper.readValue(responseBodyString, Map.class);
if (Boolean.TRUE.equals(orderResponse.get("success"))) {
Map<String, Object> orderData = (Map<String, Object>) orderResponse.get("data");
String orderId = String.valueOf(orderData.get("id"));
logger.info("Order created. Order ID: {}", orderId);
// Проверяем, есть ли ссылка на скачивание сразу
if ("ok".equalsIgnoreCase(String.valueOf(orderData.get("status"))) && orderData.containsKey("link")) {
String downloadLink = (String) orderData.get("link");
logger.info("Order processed immediately. Download link: {}", downloadLink);
return downloadAndSaveAccounts(downloadLink, orderId);
} else if ("pending".equalsIgnoreCase(String.valueOf(orderData.get("status")))) {
logger.info("Order is pending. Will try to fetch details later. Order ID: {}", orderId);
// Пауза перед запросом статуса/деталей заказа
try {
Thread.sleep(10000); // 10 секунд, можно настроить
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.warn("Delay interrupted", e);
}
return fetchOrderDownloadLink(orderId);
} else {
logger.error("Order status is not 'ok' or 'pending', or link is missing. Order data: {}", orderData);
return false;
}
} else {
Object errorDetails = orderResponse.get("data");
String errorMessage = "Unknown API error during order creation.";
if (errorDetails instanceof Map) {
Map<?, ?> errorDataMap = (Map<?, ?>) errorDetails;
if (errorDataMap.containsKey("message")) {
errorMessage = (String) errorDataMap.get("message");
}
logger.error("Order creation was not successful. API Error: '{}'. Full error data: {}", errorMessage, errorDetails);
} else {
// Fallback if 'data' is not as expected or missing, but success is false
logger.error("Order creation was not successful: {}. Full API response: {}", errorMessage, orderResponse);
}
return false;
}
} catch (IOException e) {
logger.error("Error creating order", e);
return false;
}
}
// Новый метод для получения ссылки на скачивание заказа
private boolean fetchOrderDownloadLink(String orderId) {
logger.info("Fetching download link for order ID: {}", orderId);
HttpUrl.Builder urlBuilder = HttpUrl.parse(API_BASE_URL + "/order/download").newBuilder();
urlBuilder.addQueryParameter("key", apiKey);
urlBuilder.addQueryParameter("id", orderId);
Request request = new Request.Builder()
.url(urlBuilder.build())
.header("Accept", "application/json")
.get() // Используем GET
.build();
logger.info("Requesting order download link: {}", request.url());
try (Response response = httpClient.newCall(request).execute()) {
String responseBodyString = response.body() != null ? response.body().string() : null;
logger.info("Fetch order download link response status: {}, body: {}", response.code(), responseBodyString);
if (!response.isSuccessful() || responseBodyString == null) {
logger.error("Failed to get order download link for {}: {} - {}", orderId, response.code(), responseBodyString);
return false;
}
Map<String, Object> downloadResponse = objectMapper.readValue(responseBodyString, Map.class);
if (Boolean.TRUE.equals(downloadResponse.get("success"))) {
Map<String, Object> data = (Map<String, Object>) downloadResponse.get("data");
if (data != null && data.containsKey("link")) {
String downloadLink = (String) data.get("link");
logger.info("Successfully fetched download link for order {}: {}", orderId, downloadLink);
return downloadAndSaveAccounts(downloadLink, orderId); // Используем существующий метод для скачивания
} else {
// Это может произойти, если заказ все еще не готов, или API вернуло неожиданный формат
logger.error("Download link not found in response for order {}. Data: {}", orderId, data);
// Можно добавить логику повторных попыток или проверки статуса через order/status
// Например, если API может вернуть статус заказа через этот же эндпоинт, это нужно учесть
// В документации 'order/download' должен возвращать 'link' при успехе
// Если API может вернуть здесь {"success":true, "data":{"status":"pending"}}, нужна доп. обработка
return false;
}
} else {
Object errorDetails = downloadResponse.get("data");
String errorMessage = "Failed to fetch download link.";
if (errorDetails instanceof Map) {
Map<?, ?> errorDataMap = (Map<?, ?>) errorDetails;
if (errorDataMap.containsKey("message")) {
errorMessage = (String) errorDataMap.get("message");
}
}
logger.error("Fetching download link was not successful for order {}. API Error: '{}'. Full error data: {}", orderId, errorMessage, errorDetails);
return false;
}
} catch (IOException e) {
logger.error("Error fetching download link for order {}", orderId, e);
return false;
}
}
private boolean downloadAndSaveAccounts(String fileUrl, String orderId) {
Request request = new Request.Builder()
.url(fileUrl) // Используем полный URL как есть
// .header("Authorization", "Bearer " + apiKey) // В документации не указано, что для скачивания файла нужен ключ API, если ссылка уже получена.
// Если загрузка по прямой ссылке требует авторизации, ее нужно будет добавить.
// Обычно, если ссылка временная/подписанная, доп. авторизация не нужна.
.build();
logger.info("Downloading account data from URL: {}", fileUrl);
try (Response response = httpClient.newCall(request).execute()) {
if (!response.isSuccessful() || response.body() == null) {
logger.error("Failed to download account file from {}: {} - {}", fileUrl, response.code(), response.body() != null ? response.body().string() : "Empty body");
return false;
}
try (InputStream inputStream = response.body().byteStream()) {
Path filePath = Paths.get(accountsDir, "order_" + orderId + "_accounts.txt");
try (FileOutputStream outputStream = new FileOutputStream(filePath.toFile())) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
}
logger.info("Accounts data saved to {}", filePath);
return true;
}
} catch (IOException e) {
logger.error("Error downloading or saving account file from {}", fileUrl, e);
return false;
}
}
private boolean saveAccountsFromText(String textData, String orderId) {
Path filePath = Paths.get(accountsDir, "order_" + orderId + "_accounts.txt");
try {
Files.writeString(filePath, textData);
logger.info("Accounts data saved to {}", filePath);
return true;
} catch (IOException e) {
logger.error("Error saving accounts data to {}", filePath, e);
return false;
}
}
}

View File

@ -0,0 +1,115 @@
package com.google.accountgen.service;
import jakarta.mail.*;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.search.FromTerm;
import jakarta.mail.search.SearchTerm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class EmailService {
private static final Logger logger = LoggerFactory.getLogger(EmailService.class);
private final String host;
private final int port;
private final String username;
private final String password;
private final boolean imapSsl;
// Паттерн для извлечения 6-8 значного кода Google
private static final Pattern GOOGLE_CODE_PATTERN = Pattern.compile("\\\\b(\\\\d{6,8})\\\\b");
public EmailService(String host, int port, String username, String password, boolean imapSsl) {
this.host = host;
this.port = port;
this.username = username;
this.password = password;
this.imapSsl = imapSsl;
}
/**
* Ищет и извлекает код подтверждения Google из писем.
* @param targetEmail Адрес резервной почты, на которую было отправлено письмо.
* @param targetPassword Пароль от резервной почты.
* @return Код подтверждения или null, если не найден.
*/
public String fetchGoogleVerificationCode(String targetEmail, String targetPassword) {
if (host == null || host.isEmpty() || targetEmail == null || targetEmail.isEmpty()) {
logger.warn("IMAP host or target email is not configured. Cannot fetch verification code.");
return null;
}
Properties props = new Properties();
props.put("mail.store.protocol", "imap");
props.put("mail.imap.host", host);
props.put("mail.imap.port", String.valueOf(port));
if (imapSsl) {
props.put("mail.imap.ssl.enable", "true");
// Можно добавить specific ciphers if needed, e.g.:
// props.put("mail.imap.ssl.ciphersuites", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256");
} else {
props.put("mail.imap.starttls.enable", "true"); // Предполагаем STARTTLS если не SSL
}
Session emailSession = Session.getInstance(props);
// emailSession.setDebug(true); // для отладки
try (Store store = emailSession.getStore("imap")) {
store.connect(this.username, this.password); // Используем общие креды для подключения к серверу
// если для каждого ящика свои, то targetEmail, targetPassword
try (Folder inbox = store.getFolder("INBOX")) {
inbox.open(Folder.READ_ONLY);
// Ищем письма от Google
SearchTerm sender = new FromTerm(new InternetAddress("no-reply@accounts.google.com"));
Message[] messages = inbox.search(sender);
logger.info("Found {} messages from no-reply@accounts.google.com", messages.length);
// Ищем код в последних сообщениях
for (int i = messages.length - 1; i >= 0 && i >= messages.length - 5; i--) { // Проверяем последние 5 писем
Message message = messages[i];
logger.debug("Processing message: {}", message.getSubject());
String content = "";
Object msgContent = message.getContent();
if (msgContent instanceof String) {
content = (String) msgContent;
} else if (msgContent instanceof Multipart) {
Multipart multipart = (Multipart) msgContent;
for (int j = 0; j < multipart.getCount(); j++) {
BodyPart bodyPart = multipart.getBodyPart(j);
if (bodyPart.isMimeType("text/plain")) {
content = (String) bodyPart.getContent();
break;
}
// Можно добавить обработку text/html, если потребуется
}
}
Matcher matcher = GOOGLE_CODE_PATTERN.matcher(content);
if (matcher.find()) {
String code = matcher.group(1);
logger.info("Found Google verification code: {}", code);
return code;
}
}
logger.warn("Google verification code not found in recent emails from no-reply@accounts.google.com");
}
} catch (NoSuchProviderException e) {
logger.error("IMAP provider not found", e);
} catch (MessagingException e) {
logger.error("Messaging exception while fetching email", e);
} catch (Exception e) {
logger.error("Unexpected error while fetching email", e);
}
return null;
}
}

View File

@ -0,0 +1,45 @@
package com.google.accountgen.service;
import java.util.List;
import java.util.Random;
public class FingerprintUtils {
private static final List<String> USER_AGENTS = List.of(
// Популярные user-agent'ы Chrome/Windows/Mac/Linux
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36"
);
private static final List<String> LANGS = List.of(
"ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7",
"en-US,en;q=0.9,ru;q=0.8",
"en-GB,en;q=0.9,ru;q=0.8",
"tr-TR,tr;q=0.9,en-US;q=0.8,en;q=0.7",
"de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7"
);
private static final List<String> TIMEZONES = List.of(
"Europe/Moscow", "Europe/Istanbul", "Europe/Berlin", "America/New_York", "Asia/Almaty", "Asia/Yekaterinburg"
);
private static final List<String> WINDOW_SIZES = List.of(
"1920,1080", "1366,768", "1600,900", "1536,864", "1440,900", "1280,800"
);
private static final Random RANDOM = new Random();
public static String getRandomUserAgent() {
return USER_AGENTS.get(RANDOM.nextInt(USER_AGENTS.size()));
}
public static String getRandomLang() {
return LANGS.get(RANDOM.nextInt(LANGS.size()));
}
public static String getRandomTimezone() {
return TIMEZONES.get(RANDOM.nextInt(TIMEZONES.size()));
}
public static String getRandomWindowSize() {
return WINDOW_SIZES.get(RANDOM.nextInt(WINDOW_SIZES.size()));
}
}

View File

@ -0,0 +1,66 @@
package com.google.accountgen.service;
import org.openqa.selenium.*;
import java.util.Random;
public class HumanEmulationUtils {
private static final Random RANDOM = new Random();
// Ввод текста с задержками, ошибками и backspace
public static void typeLikeHuman(WebElement element, String text) {
StringBuilder current = new StringBuilder();
for (char c : text.toCharArray()) {
// Иногда делаем ошибку (опечатку)
if (RANDOM.nextDouble() < 0.07) {
char typo = (char) (c + 1); // простая опечатка
element.sendKeys(String.valueOf(typo));
sleep(50, 120);
element.sendKeys(Keys.BACK_SPACE);
sleep(50, 120);
}
element.sendKeys(String.valueOf(c));
current.append(c);
// Иногда жмём backspace
if (current.length() > 2 && RANDOM.nextDouble() < 0.04) {
element.sendKeys(Keys.BACK_SPACE);
current.deleteCharAt(current.length() - 1);
sleep(50, 120);
element.sendKeys(String.valueOf(c));
current.append(c);
}
sleep(70, 180);
}
}
// Случайная задержка
public static void sleep(int minMs, int maxMs) {
try {
Thread.sleep(minMs + RANDOM.nextInt(maxMs - minMs + 1));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// Случайное движение мыши (эмуляция через JS)
public static void randomMouseMove(WebDriver driver) {
if (!(driver instanceof JavascriptExecutor)) return;
int x = 50 + RANDOM.nextInt(800);
int y = 50 + RANDOM.nextInt(500);
String script = "var ev = new MouseEvent('mousemove', {clientX: " + x + ", clientY: " + y + ", bubbles: true}); document.body.dispatchEvent(ev);";
((JavascriptExecutor) driver).executeScript(script);
sleep(80, 200);
}
// Случайный клик по неключевому элементу
public static void randomClick(WebDriver driver) {
try {
java.util.List<WebElement> divs = driver.findElements(By.tagName("div"));
if (!divs.isEmpty()) {
WebElement el = divs.get(RANDOM.nextInt(divs.size()));
((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", el);
sleep(50, 150);
el.click();
}
} catch (Exception ignored) {}
}
}

View File

@ -0,0 +1,128 @@
package com.google.accountgen.service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.accountgen.config.AppProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.net.URIBuilder;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
@Service
@Slf4j
public class TwoCaptchaService {
private static final String API_URL_REQUEST = "http://2captcha.com/in.php";
private static final String API_URL_RESPONSE = "http://2captcha.com/res.php";
private static final int MAX_RETRIES = 20; // Max retries for checking captcha status
private static final long RETRY_DELAY_MS = 5000; // Delay between retries
private final String apiKey;
private final ObjectMapper objectMapper = new ObjectMapper();
public TwoCaptchaService(AppProperties appProperties) {
this.apiKey = appProperties.getCaptchaApiKey();
}
/**
* Solves a reCAPTCHA v2.
*
* @param siteKey The sitekey from the page.
* @param pageUrl The URL of the page where the captcha is present.
* @return The g-recaptcha-response token, or null if solving failed.
* @throws IOException If an I/O error occurs during HTTP communication.
* @throws InterruptedException If the thread is interrupted while waiting.
* @throws URISyntaxException If there is an error in the URI syntax.
*/
public String solveRecaptchaV2(String siteKey, String pageUrl) throws IOException, InterruptedException, URISyntaxException {
log.info("Attempting to solve reCAPTCHA v2 with sitekey: {} for URL: {}", siteKey, pageUrl);
if (apiKey == null || apiKey.isEmpty()) {
log.error("2Captcha API key is not configured.");
return null;
}
String requestId;
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
URI uri = new URIBuilder(API_URL_REQUEST)
.addParameter("key", apiKey)
.addParameter("method", "userrecaptcha")
.addParameter("googlekey", siteKey)
.addParameter("pageurl", pageUrl)
.addParameter("json", "1") // Request response in JSON format
.build();
HttpGet request = new HttpGet(uri);
log.debug("Sending request to 2Captcha: {}", uri);
requestId = httpClient.execute(request, response -> {
String responseBody = EntityUtils.toString(response.getEntity());
log.debug("2Captcha request response: {}", responseBody);
JsonNode rootNode = objectMapper.readTree(responseBody);
if (rootNode.path("status").asInt() == 1) {
return rootNode.path("request").asText();
} else {
String errorText = rootNode.path("error_text").asText("Unknown error from 2Captcha API (in.php)");
log.error("Error from 2Captcha (in.php): {}", errorText);
throw new IOException("Error from 2Captcha (in.php): " + errorText);
}
});
}
log.info("2Captcha request ID: {}", requestId);
// Poll for the result
for (int i = 0; i < MAX_RETRIES; i++) {
Thread.sleep(RETRY_DELAY_MS);
final int currentAttempt = i + 1;
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
URI uri = new URIBuilder(API_URL_RESPONSE)
.addParameter("key", apiKey)
.addParameter("action", "get")
.addParameter("id", requestId)
.addParameter("json", "1") // Request response in JSON format
.build();
HttpGet request = new HttpGet(uri);
log.debug("Polling 2Captcha for result: {}", uri);
String solution = httpClient.execute(request, response -> {
String responseBody = EntityUtils.toString(response.getEntity());
log.debug("2Captcha poll response: {}", responseBody);
JsonNode rootNode = objectMapper.readTree(responseBody);
if (rootNode.path("status").asInt() == 1) {
return rootNode.path("request").asText();
} else {
String errorText = rootNode.path("request").asText(); // Sometimes error is in 'request' field
if ("CAPCHA_NOT_READY".equals(errorText)) {
log.info("Captcha not ready yet, retrying... (Attempt {}/{})", currentAttempt, MAX_RETRIES);
return null; // Special case for retry
} else {
log.error("Error or unexpected response from 2Captcha (res.php): {}", errorText);
// Consider specific error codes like ERROR_CAPTCHA_UNSOLVABLE
if ("ERROR_CAPTCHA_UNSOLVABLE".equals(errorText)) {
throw new IOException("2Captcha reported captcha as unsolvable.");
}
throw new IOException("Error or unexpected response from 2Captcha (res.php): " + errorText);
}
}
});
if (solution != null) {
log.info("reCAPTCHA solved successfully. Token: {}", solution);
return solution;
}
}
}
log.error("Failed to solve reCAPTCHA after {} retries.", MAX_RETRIES);
return null;
}
}

View File

@ -438,4 +438,26 @@ public class YouTubeMusicHandler
return null;
}
/**
* Кликает по первому видео/треку на главной странице YouTube Music
* (используется после логина для генерации cookies/POT)
* @return true если клик успешен, иначе false
*/
public boolean playFirstTrackOrVideo() {
try {
logger.info("Пробую кликнуть по первому видео/треку на YouTube Music...");
// Явное ожидание появления кнопки play у первого видео/трека
WebElement playButton = wait.until(ExpectedConditions.elementToBeClickable(
By.cssSelector("ytmusic-responsive-list-item-renderer ytmusic-play-button-renderer")
));
playButton.click();
logger.info("Клик по первому видео/треку выполнен успешно.");
Thread.sleep(2000); // Дать время на запуск воспроизведения
return true;
} catch (Exception e) {
logger.warn("Не удалось кликнуть по первому видео/треку: {}", e.getMessage());
return false;
}
}
}

View File

@ -1,7 +1,7 @@
#sms_activate_api_key: cd64e325c15813edc722867BB6171B94
sms_activate_api_key:
sms_activate_api_key:
captcha_api_key: deeb0d134a47e88dffab6dc48a5a0906
captcha_api_key: 399fac0452b879b2dbf586fdaa919d8d
captcha_service_type: TWOCAPTCHA
retry_count: 3
@ -21,4 +21,32 @@ sms_service: SMSACTIVATE
account_count: 30
delay_between_attempts: 5000
default_country: 0
default_country: 0
darkshopping:
api_key: "209e0176b46e059bd7bec1e70c7d1811ec4d7c9a"
product_id: "113421"
purchase_quantity: 1
accounts:
directory: "accounts"
imap:
host: "imap.example.com"
port: 993
username: "your-email@example.com"
password: "your-password"
ssl: true
proxy6:
api_key: "3147c5e1c1-05102f380c-5e16a83021"
app:
useProxy: true
accounts:
directory: accounts
cookies:
directory: accounts_cookies
# ... другие существующие app свойства ...
# ... остальные существующие свойства ...

View File

@ -0,0 +1 @@

View File

@ -1,7 +1,6 @@
#sms_activate_api_key: cd64e325c15813edc722867BB6171B94
sms_activate_api_key: pPmJIKmh7RepulVySCPf
sms_activate_api_key:
captcha_api_key: deeb0d134a47e88dffab6dc48a5a0906
captcha_api_key: 399fac0452b879b2dbf586fdaa919d8d
captcha_service_type: TWOCAPTCHA
retry_count: 3
@ -21,4 +20,32 @@ sms_service: SMSACTIVATE
account_count: 30
delay_between_attempts: 5000
default_country: 0
default_country: 0
darkshopping:
api_key: "ВАШ_API_КЛЮЧ"
product_id: "113421"
purchase_quantity: 1
accounts:
directory: "accounts"
imap:
host: "imap.example.com"
port: 993
username: "your-email@example.com"
password: "your-password"
ssl: true
proxy6:
api_key: "ВАШ_API_КЛЮЧ"
app:
useProxy: true
accounts:
directory: accounts
cookies:
directory: accounts_cookies
# ... другие существующие app свойства ...
# ... остальные существующие свойства ...

View File

@ -0,0 +1 @@

Binary file not shown.

Binary file not shown.

View File

@ -1,20 +1,36 @@
com\google\accountgen\user\UserData.class
com\google\accountgen\CookieGenerator.class
com\google\accountgen\captcha\CaptchaSolver$ServiceType.class
com\google\accountgen\sms\SmsActivateService$ActivationResult.class
com\google\accountgen\registration\GoogleRegistrationSelectors.class
com\google\accountgen\registration\GoogleRegistrationHandler.class
com\google\accountgen\login\GoogleLoginHandler.class
com\google\accountgen\user\RandomUserGenerator.class
com\google\accountgen\service\AccountManager.class
com\google\accountgen\config\AppProperties$DarkShoppingProps.class
com\google\accountgen\captcha\CaptchaSolver.class
com\google\accountgen\account\GoogleAccountGenerationResult.class
com\google\accountgen\youtube\YouTubeMusicHandler.class
com\google\accountgen\service\AccountManager$AccountDetails.class
com\google\accountgen\account\AccountResult.class
com\google\accountgen\user\UserData$Gender.class
com\google\accountgen\service\DarkShoppingService.class
com\google\accountgen\cookies\CookieManager$1.class
com\google\accountgen\service\TwoCaptchaService.class
com\google\accountgen\account\GoogleAccount.class
com\google\accountgen\user\UserData.class
com\google\accountgen\config\AppProperties$Proxy6Props.class
com\google\accountgen\service\FingerprintUtils.class
com\google\accountgen\config\AppProperties.class
com\google\accountgen\registration\GoogleRegistrationHandler.class
com\google\accountgen\account\GoogleAccountGeneratorImpl.class
com\google\accountgen\sms\SmsActivateService.class
com\google\accountgen\cookies\CookieManager.class
com\google\accountgen\captcha\CaptchaSolver.class
com\google\accountgen\service\EmailService.class
com\google\accountgen\config\AppProperties$Cookies.class
com\google\accountgen\account\GoogleAccountGenerator.class
com\google\accountgen\Task.class
com\google\accountgen\account\GoogleAccountGenerationResult.class
com\google\accountgen\youtube\YouTubeMusicHandler.class
com\google\accountgen\account\AccountResult.class
com\google\accountgen\proxy\ProxyManager.class
com\google\accountgen\user\UserData$Gender.class
com\google\accountgen\account\GoogleAccount.class
com\google\accountgen\service\HumanEmulationUtils.class
com\google\accountgen\AccountProcessingService.class
com\google\accountgen\config\AppProperties$ImapProps.class
com\google\accountgen\config\AppProperties$AccountsProps.class
com\google\accountgen\account\UserData.class

View File

@ -1,17 +1,26 @@
C:\IdeaProject\cookieGenerator\src\main\java\com\google\accountgen\Task.java
C:\IdeaProject\cookieGenerator\src\main\java\com\google\accountgen\registration\GoogleRegistrationSelectors.java
C:\IdeaProject\cookieGenerator\src\main\java\com\google\accountgen\youtube\YouTubeMusicHandler.java
C:\IdeaProject\cookieGenerator\src\main\java\com\google\accountgen\user\RandomUserGenerator.java
C:\IdeaProject\cookieGenerator\src\main\java\com\google\accountgen\cookies\CookieManager.java
C:\IdeaProject\cookieGenerator\src\main\java\com\google\accountgen\proxy\ProxyManager.java
C:\IdeaProject\cookieGenerator\src\main\java\com\google\accountgen\account\GoogleAccountGeneratorImpl.java
C:\IdeaProject\cookieGenerator\src\main\java\com\google\accountgen\account\UserData.java
C:\IdeaProject\cookieGenerator\src\main\java\com\google\accountgen\sms\SmsActivateService.java
C:\IdeaProject\cookieGenerator\src\main\java\com\google\accountgen\user\UserData.java
C:\IdeaProject\cookieGenerator\src\main\java\com\google\accountgen\account\GoogleAccount.java
C:\IdeaProject\cookieGenerator\src\main\java\com\google\accountgen\account\GoogleAccountGenerator.java
C:\IdeaProject\cookieGenerator\src\main\java\com\google\accountgen\account\AccountResult.java
C:\IdeaProject\cookieGenerator\src\main\java\com\google\accountgen\account\GoogleAccountGenerationResult.java
C:\IdeaProject\cookieGenerator\src\main\java\com\google\accountgen\CookieGenerator.java
C:\IdeaProject\cookieGenerator\src\main\java\com\google\accountgen\captcha\CaptchaSolver.java
C:\IdeaProject\cookieGenerator\src\main\java\com\google\accountgen\registration\GoogleRegistrationHandler.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\CookieGenerator.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\login\GoogleLoginHandler.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\config\AppProperties.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\account\GoogleAccount.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\AccountProcessingService.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\account\AccountResult.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\service\DarkShoppingService.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\Task.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\registration\GoogleRegistrationHandler.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\service\EmailService.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\cookies\CookieManager.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\sms\SmsActivateService.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\user\UserData.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\account\GoogleAccountGeneratorImpl.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\captcha\CaptchaSolver.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\account\GoogleAccountGenerationResult.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\service\HumanEmulationUtils.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\registration\GoogleRegistrationSelectors.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\service\FingerprintUtils.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\account\GoogleAccountGenerator.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\proxy\ProxyManager.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\service\TwoCaptchaService.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\service\AccountManager.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\youtube\YouTubeMusicHandler.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\account\UserData.java
C:\Projects\generator2\cookieGenerator <20> <20><><EFBFBD><EFBFBD><EFBFBD> (2)\src\main\java\com\google\accountgen\user\RandomUserGenerator.java