Скачивание профилей с 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

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);
}
}
}