Files
cookieGenerator/src/main/java/com/google/accountgen/AccountProcessingService.java

495 lines
28 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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