commit 6a44090153e278aeba40a2781d67a4af7fc8c6b7 Author: Jory Severijnse Date: Thu Jun 18 10:53:32 2026 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/README.md b/README.md new file mode 100644 index 0000000..140d204 --- /dev/null +++ b/README.md @@ -0,0 +1,88 @@ +# Multi-Site Sign-Up Automation Script + +This script automates the process of signing up for multiple websites using a single configuration file. It is designed to be flexible and resilient, using advanced techniques to mimic human behavior. + +--- + +## How It Works + +The script reads its configuration from the `config.toml` file. It launches a browser using your real user profile to avoid bot detection, and then iterates through the list of sites you've defined, filling out and submitting the sign-up forms. + +--- + +## Configuration (`config.toml`) + +This is the main file you will edit to control the script. It has the following sections: + +### 1. `[browser]` Section + +This section controls the browser's behavior. + +- `userDataDir` (Required): The absolute path to your Chrome/Chromium user profile. This is critical for avoiding bot detection. +- `userAgent` (Optional): A string to make the browser appear as a specific version (e.g., a Windows machine). If left blank, a random user agent is used. + +**Example:** +```toml +[browser] +userDataDir = "/home/someone/.config/chromium" +userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" +``` + +### 2. `[global_credentials]` Section + +Define a default set of credentials here. The script will use these for any site that does not have its own specific credentials defined. + +- `username` +- `email` +- `password` +- `age` (or any other custom field) + +**Example:** +```toml +[global_credentials] +username = "MyGlobalUsername" +email = "my.global.email@gmail.com" +password = "MyGlobalPassword123!" +age = "28" +``` + +### 3. `[[sites]]` Blocks + +Each site you want to process is defined in its own `[[sites]]` block. + +- `name`: A unique name for the site (e.g., "CameraBoys"). +- `category`: An organizational tag (e.g., "Social", "Financial"). +- `url`: The direct URL to the sign-up page. +- `needsPreSubmitAction`: Set to `true` for sites that have a GDPR/cookie popup. Currently, this is only configured for "CameraBoys". + +#### `[sites.locators]` Subsection + +This is where you define the form fields for the site. The script will dynamically try to fill **any field you list here**. + +- **Easy Mode:** Simply find the element in your browser's developer tools, right-click, "Copy element", and paste the entire HTML tag as the value. +- **Advanced Mode:** You can also use a standard CSS selector. + +**Supported Fields:** `username`, `password`, `email`, `confirmEmail`, `age`, or any other custom field. +*Note: For `confirmEmail`, the script will automatically use the `email` value.* + +**Example:** +```toml +[sites.locators] +email = '' +confirmEmail = '' +age = '#user_age' # You can still use CSS selectors +submit = '' +``` + +#### `[sites.credentials]` Subsection (Optional) + +If a site requires a unique login, you can define it here. This will **override** the `[global_credentials]` for this site only. + +**Example:** +```toml +[sites.credentials] +username = "SiteSpecificUser" +email = "site.specific.email@gmail.com" +password = "SitePassword789" +age = "42" +``` diff --git a/config.toml b/config.toml new file mode 100644 index 0000000..8abaab5 --- /dev/null +++ b/config.toml @@ -0,0 +1,46 @@ +# Configuration file for the multi-site sign-up script. + +[browser] +userDataDir = "/home/someone/.config/chromium" +userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" + +# Global credentials to be used for any site that doesn't have its own specific credentials. +[global_credentials] +username = "Thisisgebruikersnaam" +email = "thisisanothertestbusiness@hotmail.com" +password = "Fuckingstrongpassword123!" +phone = "+31610488717" +firstName = "Thisfirstname" +lastName = "Thislastname" + +[[sites]] +name = "Dutch News Test" +category = "Newsletter" +url = "https://www.dutchnews.nl/dutchnews-newsletter-signup/" +needsPreSubmitAction = false +# autoDetect is enabled by default + +[[sites]] +name = "Immaculate Vegan" +category = "Newsletter" +url = "https://immaculatevegan.com/blogs/magazine" +needsPreSubmitAction = false + +[[sites]] +name = "Gate6" +category = "Newsletter" +url = "https://gate6.vn/" +needsPreSubmitAction = false + +[[sites]] +name = "Kulala Land" +category = "Newsletter" +url = "https://kulalaland.com/" +needsPreSubmitAction = false + +[[sites]] +name = "sneakywholefoods" +category = "Newsletter" +url = "https://www.sneakywholefoods.com/" +needsPreSubmitAction = false + diff --git a/email_subscription.json b/email_subscription.json new file mode 100644 index 0000000..5945ecf --- /dev/null +++ b/email_subscription.json @@ -0,0 +1,1092 @@ +[ + { + "url": "https://profile.usatoday.com/newsletters/sports", + "verified": true, + "input_fields": { + "email": [ + { + "id": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "class": "primary" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://uptain.de/en/blog/how-online-shops-generate-newsletter-subscribers", + "verified": true, + "input_fields": { + "email": [ + { + "name": "wpforms[fields][1]" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "name": "wpforms[submit]" + } + ], + "radios": [], + "checkboxes": [ + { + "name": "wpforms[fields][3][]" + } + ], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://www.finnishdesignshop.com/en-us/newsletters", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "class": "newsletter-ajax-submit" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://www.thecut.com/promo/cut-shop-newsletter-sign-up.html", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "value": "Sign up" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://www.uniqlo.com/sg/en/member/e-news/subscription", + "verified": true, + "input_fields": { + "email": [ + { + "name": "subscribe" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "class": "primary" + } + ], + "radios": [], + "checkboxes": [ + { + "name": "newsletter" + } + ], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://newsletter.defitimes.io/about", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "class": "subscribe-btn" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://nocodelandscape.beehiiv.com/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "value": "Subscribe" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://newsletter.spaceknown.com/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "value": "Subscribe" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://www.ohmyrockness.com/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "value": "Sign Up >" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://www.axios.com/signup/pro-rata", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "value": "Subscribe" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://salesstoic.com/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "value": "Start crushing it in sales..." + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://fastlane-fleet.beehiiv.com/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "value": "Don't miss out on the ride of a lifetime!" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://geekout.mattnavarra.com/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "value": "Subscribe (It's free!)" + } + ], + "radios": [], + "checkboxes": [ + { + "class": "text-lp-main-signup-cta-color" + } + ], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://thehumansintheloop.substack.com/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "class": "subscribe-btn" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://newsletter.betterstack.com/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "class": "subscribe-btn" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://unconventional-empires.beehiiv.com/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "value": "Join Us! " + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://dotnetnews.co/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "EMAIL" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "class": "js-submit-button" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://substack.com/signup", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "class": "primary" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://www.aididthat.com/subscribe", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "value": "Subscribe" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://dharab.com/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "EMAIL" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "name": "submit" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://palealetravel.beehiiv.com/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "value": "Subscribe" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://7x7.news/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "class": "subscribe-btn" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://astukari.substack.com/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "class": "subscribe-btn" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://jbrworks.substack.com/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "class": "subscribe-btn" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://webtoolsweekly.com/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "EMAIL" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "name": "subscribe" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://techproductivity.co", + "verified": true, + "input_fields": { + "email": [ + { + "name": "EMAIL" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "name": "subscribe" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://vscode.email", + "verified": true, + "input_fields": { + "email": [ + { + "name": "EMAIL" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "name": "subscribe" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://standardhoy.substack.com", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "class": "subscribe-btn" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://www.badgerandyote.com", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "value": "Subscribe" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://insightsinnovationecon.substack.com", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "class": "subscribe-btn" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://kapansrt.substack.com", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "class": "subscribe-btn" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://thedonut.co/news-personality-quiz/results/?medium=email&interests=world", + "verified": true, + "input_fields": { + "email": [ + { + "name": "emailAddress" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "class": "NewsQuizSubscribeBar-module--button--c5311" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://coinbrain.beehiiv.com/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "value": "Subscribe" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://www.insidesmallgiants.com/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "class": "subscribe-btn" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://deascentiis.substack.com/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "class": "subscribe-btn" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://www.attnseek.com/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "value": "Subscribe" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://daily.thecore.email/subscribe", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "value": " Subscribe" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://worldofai.beehiiv.com?ref=amazingnewsletters.com&utm_source=amazingnewsletters.com", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "value": "Subscribe" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://the-baseline.beehiiv.com/?ref=amazingnewsletters.com&utm_source=amazingnewsletters.com", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "value": "Subscribe" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://thisweekinreact.com/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email_address" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "class": "formkit-submit" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://linkedinprompts.beehiiv.com/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "value": "Subscribe" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://www.forstarters.co/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "value": "Subscribe" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://www.theatlantic.com/newsletters/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "class": "NewslettersSignupBar_button__H1vw5" + } + ], + "radios": [], + "checkboxes": [ + { + "class": "NewsletterCard_buttonIcon__gCltK" + } + ], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://account.forbes.com/newsletters", + "verified": true, + "input_fields": { + "email": [ + { + "class": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "class": "subscribe-button" + } + ], + "radios": [], + "checkboxes": [ + { + "value": "DailyDozen" + } + ], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://www.keyence.com.sg/enews/subscribe/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "enewsEmailAddress" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "name": "subscribe" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://news.un.org/en/content/un-newsletter-subscribe", + "verified": true, + "input_fields": { + "email": [ + { + "name": "EMAIL" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "name": "subscribe" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://www.bruggpipes.com/en/newsletter-sign-up/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "tx_powermail_pi1[field][vorname]" + }, + { + "name": "tx_powermail_pi1[field][name]" + }, + { + "name": "tx_powermail_pi1[field][e_mail]" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "value": "Subscribe" + } + ], + "radios": [ + { + "value": "Mr." + } + ], + "checkboxes": [ + { + "name": "tx_powermail_pi1[field][datenschutzerklaerung][]" + } + ], + "selections": [], + "wait": 5 + } + }, + { + "url": "https://news.nus.edu.sg/subscribe-to-email-newsletter/", + "verified": true, + "input_fields": { + "email": [ + { + "name": "email" + } + ], + "username": [], + "phone": [], + "submit": [ + { + "id": "submit_button-ppmail" + } + ], + "radios": [], + "checkboxes": [], + "selections": [], + "wait": 5 + } + } +] \ No newline at end of file diff --git a/eng.traineddata b/eng.traineddata new file mode 100644 index 0000000..6d11002 Binary files /dev/null and b/eng.traineddata differ diff --git a/failed_sites.txt b/failed_sites.txt new file mode 100644 index 0000000..cc66a39 --- /dev/null +++ b/failed_sites.txt @@ -0,0 +1,7 @@ +DATE | TIME | WEBSITE | REASON +---------------------------------------------------------------------------------------------------- +15/02/2026 | 12:40:49 | Dutch News Test (https://www.dut | Form still visible after submission (likely failed) +15/02/2026 | 12:40:49 | Immaculate Vegan (https://immacu | Form still visible after submission (likely failed) +15/02/2026 | 12:40:49 | Gate6 (https://gate6.vn/) | Form still visible after submission (likely failed) +15/02/2026 | 12:40:49 | Kulala Land (https://kulalaland. | No email field detected +15/02/2026 | 12:40:49 | sneakywholefoods (https://www.sn | Not a newsletter signup form diff --git a/index.js b/index.js new file mode 100644 index 0000000..84590fc --- /dev/null +++ b/index.js @@ -0,0 +1,146 @@ +const fs = require('fs'); +const path = require('path'); +const puppeteer = require('puppeteer-extra'); +const StealthPlugin = require('puppeteer-extra-plugin-stealth'); +puppeteer.use(StealthPlugin()); + +const { loadConfig, loadSitesFromFile } = require('./src/config'); +const { processSite } = require('./src/processor'); +const { createCLI, parseCLI } = require('./src/cli'); + +async function main() { + const cliOptions = parseCLI(); + + if (cliOptions.help) { + createCLI().help(); + return; + } + + const config = loadConfig(); + let sitesToProcess = config.sites || []; + + if (cliOptions.file) { + console.log(`Loading sites from: ${cliOptions.file}`); + sitesToProcess = loadSitesFromFile(cliOptions.file); + console.log(`Loaded ${sitesToProcess.length} sites from file`); + } + + const logFile = cliOptions.log ? path.join(__dirname, `run_${new Date().toISOString().replace(/[:.]/g, '-')}.log`) : null; + + if (cliOptions.target) { + sitesToProcess = sitesToProcess.filter(site => + site.name.toLowerCase().includes(cliOptions.target.toLowerCase()) || + site.url.toLowerCase().includes(cliOptions.target.toLowerCase()) + ); + if (sitesToProcess.length === 0) { + console.error(`No site found matching "${cliOptions.target}"`); + return; + } + console.log(`Targeting ${sitesToProcess.length} site(s)`); + } + + if (cliOptions.keepOpen) { + console.log('Keep-open mode: Tabs will stay open for verification'); + } + + const browserConfig = config.browser || {}; + const globalCreds = config.global_credentials || {}; + const userDataDir = browserConfig.userDataDir; + + if (!userDataDir) { + console.error('userDataDir not set in config.toml'); + return; + } + + if (cliOptions.log && logFile) { + const originalLog = console.log; + const originalError = console.error; + const originalWarn = console.warn; + + const writeToLog = (type, args) => { + const message = args.map(arg => typeof arg === 'string' ? arg : JSON.stringify(arg)).join(' '); + fs.appendFileSync(logFile, `[${new Date().toISOString()}] [${type}] ${message}\n`); + }; + + console.log = (...args) => { writeToLog('LOG', args); originalLog.apply(console, args); }; + console.error = (...args) => { writeToLog('ERROR', args); originalError.apply(console, args); }; + console.warn = (...args) => { writeToLog('WARN', args); originalWarn.apply(console, args); }; + console.log(`Logging to: ${logFile}`); + } + + const failedSites = []; + const processedCount = { success: 0, failed: 0 }; + + const browser = await puppeteer.launch({ + headless: false, + userDataDir: userDataDir, + args: ['--start-maximized', '--no-sandbox', '--disable-setuid-sandbox'] + }); + + try { + console.log(`\nProcessing ${sitesToProcess.length} sites...\n`); + + const allResults = []; + + for (let i = 0; i < sitesToProcess.length; i++) { + const site = sitesToProcess[i]; + console.log(`[${i + 1}/${sitesToProcess.length}]`); + + const result = await processSite(browser, site, globalCreds, browserConfig, cliOptions.keepOpen, cliOptions.dryRun); + + const siteResult = { + name: site.name, + url: site.url, + success: !result.siteFailed, + reason: result.failureReason || 'Success', + timestamp: new Date().toISOString() + }; + + allResults.push(siteResult); + + if (result.siteFailed) { + failedSites.push(siteResult); + processedCount.failed++; + } else { + processedCount.success++; + } + + // Write results after each site + const reportPath = path.join(__dirname, 'site_results.txt'); + const now = new Date(); + const dateStr = now.toLocaleDateString('en-GB'); + const timeStr = now.toLocaleTimeString('en-GB'); + + let reportContent = ''; + if (!fs.existsSync(reportPath)) { + reportContent = 'DATE | TIME | STATUS | WEBSITE | RESULT\n'; + reportContent += '-'.repeat(110) + '\n'; + } + + const website = `${site.name} (${site.url})`.substring(0, 32).padEnd(32); + const status = siteResult.success ? 'SUCCESS' : 'FAILED'; + reportContent += `${dateStr} | ${timeStr} | ${status} | ${website} | ${siteResult.reason}\n`; + + fs.appendFileSync(reportPath, reportContent); + console.log(` -> Result written to site_results.txt`); + + await new Promise(r => setTimeout(r, 1000)); + } + + console.log(`\n${'='.repeat(70)}`); + console.log(`Done: ${processedCount.success} success, ${processedCount.failed} failed`); + console.log(`Full results: site_results.txt`); + + } catch(error) { + console.error('Fatal error:', error.message); + } finally { + if (cliOptions.keepOpen) { + console.log(`\nBrowser kept open for verification`); + console.log(` Close browser manually when done`); + } else { + await browser.close(); + } + } +} + +main(); diff --git a/list.txt b/list.txt new file mode 100644 index 0000000..7a41dbd --- /dev/null +++ b/list.txt @@ -0,0 +1,354 @@ +https://immaculatevegan.com/blogs/magazine +https://gate6.vn/ +https://www.sprintzeal.com/ +https://kulalaland.com/ +https://www.goplaces.co.uk/ +https://www.fiftyfiftybottles.com/ +https://jubina.com/blog +https://www.sneakywholefoods.com/ +https://vegnews.com/signup +https://yonayonabeerworks.com/ +https://www.louispowersportsonline.com/ +https://www.hairworthy.co.uk/ +https://endotaspa.com.au/ +https://prestigify.com/ +https://nobones.co/ +https://www.sofamania.com/ +https://dimoradellebalze.com/en/restaurant/ +https://www.42photo.com/ +https://ifinishedmybasement.com/ +https://tettixgames.com/ +https://www.corinnekai.com/raw-honey-raw-self-newsletter/ +https://www.blendnwhip.com/ +https://www.brookebowlin.com/nuance-required +https://www.thatvegandad.net/ +https://comhwa.org.au/blog/newsletters/ +https://hiutdenim.co.uk/ +https://local-lovely.com/ +https://vituswholefoods.com/ +https://theoriginalshabby.it/ +https://rothys.com/en-au +https://www.trustbasket.com/ +https://chelseabrice.com/ +https://wolfsgamingblog.com/ +https://www.leadfuze.com/blog/ +https://thedefiant.io/ +https://www.entrepreneur.com/ +https://pantproject.com/ +https://www.terrebleu.ca/ +https://www.shopify.com/in/blog/shopify-stores +https://www.skooob.com/ +https://mremountain.com/ +https://uncommonjames.com/ +https://www.axios.com/signup/tampa-bay +https://www.dietassassinista.com/blog/tag/African+Cuisine +https://miriamporter.ca/ +https://uppercasemagazine.com/ +https://www.mercadodasrosas.com/ +https://mybageecha.com/ +https://bebemoss.com/ +https://linkwithin.com/ +https://www.mediocrechef.com/newsletter +https://www.trainheroic.com/blog/rdl-for-posterior-chain-development/ +https://www.duatravel.com/ +https://youthiapa.com/ +https://www.prolinerangehoods.com/ +https://inthecove.com.au/sign-up-for-newsletter/ +https://thevegan8.com/ +https://newsinfrench.org/ +https://www.wheelpros.com/ +https://eatbetterco.com/ +https://www.925express.com/ +https://www.kurandawholefoods.com.au/blogs/blog-posts +https://unitedbyblue.com/ +https://www.lifx.com.au/ +https://www.weareunderground.com/ +https://easyeyesound.com/ +https://fashionbrainacademy.com/blog-vs-newsletter/ +https://www.myburntorange.com/ +https://vituswholefoods.com/blogs/news +https://www.givemetap.com/ +https://www.lunchskins.com/ +https://www.gymlaunch.com/blog-categories/newsletter +https://yumetwins.com/blog/kawaii-hotel-rooms-in-japan +https://www.artistsandillustrators.co.uk/ +https://tokyovegangyoza.com/en/pages/newsletter-registration +https://thegoodlifefrance.com/newsletter-sign-up/ +https://www.novethic.fr/economie-et-social/business-model-en-transition/rse-managers-pilotage-entreprise +https://www.35mmc.com/ +https://therainbowvision.com/ +https://www.customgoldgrillz.com/ +https://tofinosoapcompany.com/ +https://store.lonestarlearning.com/ +https://mumcentral.com.au/ +https://www.velasca.com/ +https://satyaorganics.com/ +https://popitout.in/ +https://www.afar.com/travel-tips/the-best-restaurants-in-new-zealand +https://www.mindbodygreen.com/ +https://fourcolumnsofabalancedlife.com/ +https://www.relishthebite.com/ +https://www.airportappliance.com/ +https://quedelibros.com/ +https://www.gazoakleychef.com/ +https://egunsifoods.com/ +https://www.saintalfred.com/ +https://www.wholisticallyhannah.com/all/welcome-to-wholistically-hannah +https://glamout.com.mx/ +https://mommuseum.org/sign-up-for-the-newsletter/ +https://www.greenseashells.com/ +https://jessicainthekitchen.com/ +https://www.carlazampatti.com.au/ +https://www.ricarda.com/ +https://www.supercutekawaii.com/2023/07/kawaii-japan-trip-planning-resources/ +https://www.devilinspired.com/blog/Harajuku-Fashion-Japanese-Street-Fashion/what-is-jirai-kei.html +https://leehamnews.com/ +https://highfivevape.com/ +https://www.loveandlemons.com/subscribe/ +https://finduslost.com/ +https://makeupformelaningirls.com/ +https://gosvish.com/ +https://bruvi.com/ +https://120percento.com/pages/linen-pants +https://www.tsw.com/ +https://economics-online.myshopify.com/ +https://www.captaincookcruisesfiji.com/blog/holidaying-in-fiji-8-travel-blogs-to-read +https://www.thetravelpockets.com/new-blog/tag/Sanrio +https://www.beafunmum.com/ +https://willowltd.com/ +https://www.adolfodominguez.com.au/ +https://laviebami.com/tag/west-african-food/ +https://childandfamilyblog.com/ +https://www.thehimalayanorganics.in/ +https://labelsfashion.com/ +https://theoverwhelmedmommy.com/ +https://headsupfortails.com/ +https://www.healthmates.com.au/about/monthly-newsletter +https://www.cgriver.com/ +https://theholisticgardenblog.com/ +https://www.theguthealthdoctor.com/subscribe +https://djurrattsalliansen.se/hjalp-djuren/lev-djurvanligt/bli-vegan/ +https://www.blackbykenm.com/ +https://shop.smeg.com.au/ +https://hbx.com/?no-direct=1 +https://bonnelife.com/ +https://matthiaskamann.com/blog/ +https://www.justherbs.in/ +https://gaijinpot.com/ +https://wolfsgamingblog.com/ +https://scanwp.net/blog/newsletter-thank-you/ +https://120percento.com/ +https://fausto.in/ +https://sartoriadilusso.shop/designers/off-white +https://kawaiibabe.com/ +https://www.dubwheels.com/ +https://www.nextfpv.com.au/ +https://unarosevegan.ca/welcome-to-my-blog-about-una-rose-vegan/ +https://thecanadianafrican.com/ +https://kindahome.co.ao/ +https://clutchchairz.com/ +https://whereiam.blog/ +https://www.dandelionchocolate.com/ +https://zeroinfy.in/ +https://meowmeowtweet.com/ +https://www.phoebespurefood.com/ +https://www.greenheartgames.com/ +https://freshersjobz.com/ +https://www.prashantisarees.com/ +https://www.snitch.co.in/ +https://www.vervecoffee.com/ +https://homesteadmuseum.blog/ +https://bestfriendsclub.ca/ +https://thenelgroup.au/blogs/news +https://www.goodrx.com/health +https://barekind.co.uk/ +https://flourist.com/ +https://www.belindainternational.com/ +https://www.wigsandgrace.com/ +https://www.eatfitgo.com/ +https://highfivepins.com/ +https://www.duroflexworld.com/ +https://blkandbold.com/ +https://hiyou.co/ +https://www.respectatwork.gov.au/ +https://www.thegayvegans.com/general/ +https://woodfordstatebank.com/investments.html +https://elpasomatters.org/2023/01/18/join-el-paso-matters-book-club-celebrate-el-pasos-authors/ +https://norte.ventures/ +https://www.rachelcampbellphotography.net/ +https://www.leppfarmmarket.com/contact/ +https://panda-patisserie.com/ +http://studiomspace.ca/ +https://busbeestyle.com/travel/ +https://gaysintown.com/ +https://www.hockerty.com/en-au/blog/tuxedo-vs-suit +https://www.everyonehatesmarketers.com/ +https://chelseabrice.com/ +https://www.breitbart.com/newsletters/ +https://www.ableton.com/en/subscribe-to-newsletter/ +https://www.texastribune.org/ +https://www.biblechristiansociety.com/newsletter +https://biggayhudsonvalley.com/contact/newsletter-signup/ +https://odin-engineering.dk/nyhedsbrev/ +https://gardehvalsoe.dk/ +https://aerligebent.dk/bil-jokes +https://eyda.dk/ +http://yourbusinessgazette.com/ +https://usacorrespondent.com/ +https://themarketholders.com/ +https://tldr.tech/ai +https://productmanager.jobs/newsletter +https://www.sartoricheese.com/sweepstakes/ +https://mosbrew.ru/posts/subscribe/ +https://vua-lya.ru/shares/1289/ +https://gifts.ru/about/subscribe +http://www.auto-neva.ru/rassilki +https://www.mann-ivanov-ferber.ru/samorazvitie-books/subscribe/ +https://sisters-help.ru/ +https://teatrkukoludm.ru/ +https://rikkitikkishop.dk/tilmeld-nyhedsbrev/ +https://dollar-king.net/bear-light-up.html +https://fleshbot.com/ +https://signup.dropout.tv/ +https://www.salon.com/topic/incels +https://pornstar.love/newsletter/ +https://intothewildernessblog.com/subscribe/ +https://itslisawelsh.systeme.io/sexual-confidence-guide +https://www.familyhandyman.com/newsletter/ +https://adultindustry.news/ +https://malestockroom.com/ +https://www.cindyscharkey.com/newsletter +https://www.matthewlyonsauthor.com/newsletter +https://pamutlabor.hu/shop/hobbi-erdeklodes-noi-hosszitott-polo +https://assodeclic.com/ +https://www.xexo.it/calendari/4494-calendario-porn-2019.html +https://www.siciliafoodporn.it/27-selezione-sicilia-food-porn +https://www.secrets-center.ru/ +https://www.vkostume.ru/catalog/podarki/ +https://ml.city.yokohama.lg.jp/sympa/subscribe/morinews?previous_action=info +https://ucon-acrobatics.jp/pages/newsletter +https://www.hover.com/newsletter +https://okcthunderwire.usatoday.com/newsletters +https://www.philips.co.jp/myphilips/newsletter-registration-campaign17 +https://www.marni.com/ja-jp/ +https://www.hasselblad.com/ja-jp/about/contact/ +https://kuwaii.com.au/ +https://ideasforgood.jp/mailmagazine/ +https://www.buscounchollo.com/ +https://www.uniqlo.com/es/es/content/bienvenido.html +https://www.hiperdino.es/folletos/ +https://darlingillustrations.com/newsletter-subscription/ +https://gnaraloo.org/subscribe-to-the-gnaraloo-wilderness-foundation-newsletter/ +https://www.farmandwilderness.org/newsletter-signup +https://www.waxit.com.au/pages/waxit-newsletter +https://practicalmotoring.com.au/newsletter-subscription/ +https://www.niba.com.au/newsletter +https://www.iaisweb.org/news-and-events/newsletter/newsletter-subscribe/ +https://crooked.com/tag/incels/ +https://www.maltego.com/blog/everything-to-know-about-ransomware/ +https://thewalrus.ca/tag/immigration/ +https://www.yourdarlingstyle.com/newsletter +https://aidendarlingharbour.com.au/sign-up/ +https://darlingshine.com/pages/watch-us +https://www.monaropost.com.au/newsletter-sign-up-form +https://www.csiro.au/ +https://www.darlingwest.com.au/ +https://www.ivywood.ie/shop/events/a-making-herbal-remedies-practical-course-detail +https://www.ninasjewellery.com.au/ +https://thekawaiishoppu.com/en-au/blogs/news +https://www.pernillecorydon.com/subscribe-to-newsletter +https://www.tokudeals.com/ +https://hands.com.au/ +https://medicalxpress.com/tags/herbal+medicines/ +https://www.coffeeco.com.au/ +https://urbanattitude.com.au/ +https://albathermalsprings.com.au/alba-experiences/spa/herbal-healer-90-min/ +https://www.adultshop.com/ +https://soapsweetie.com/ +https://metropolisbookshop.com.au/ +https://momnemcoffee.com/pages/newsletter +https://www.ricky.com/pages/newsletter +https://www.vittoriacoffee.com/blogs/news/tagged/coffee-blog +https://www.comeasyouare.com/pages/subscribe-to-our-newsletter +https://lioness.io/pages/subscribe +https://www.sexshop365.co.uk/catalog/newsletters.php +https://www.marelljewellery.com.au/newsletter +https://www.bevilles.com.au/ +https://www.bvibe.com/subscribe +https://www.kawaiibox.com/ +https://www.jekkas.com/ +https://thedistrictherbalist.co.uk/herbal-resources/newsletter/ +https://www.seattlecoffeegear.com/pages/subscribe +https://sokawaii.com/ +https://yumetwins.com/ +https://sararobertsson.com/pages/newsletter +https://www.steepedcontent.com/ +https://www.passionfruitshop.com.au/ +https://www.kiwiherb.com.au/contact/subscribe-to/ +https://www.nataliemariejewellery.com/ +https://alternativebrewing.com.au/blogs/news +https://www.sorujewellery.com/en-au/pages/sign-up +https://coffeealchemy.com.au/blogs/news +https://seaglassjewellery.com.au/newsletter-sign-up/ +https://www.atlantacoffeeshops.com/newsletter +https://www.bibivandervelden.com/pages/newsletter +https://joycejewellery.com/pages/signup-newsletter-free-postage +https://theherborium.com.au/collections/herbal-remedies +https://www.grahams.com.au/ +https://www.the-herb-guide.com/herb-guide-newsletter.html +https://embracejewellery.com/pages/subscribe +https://www.aldeacoffee.com/blogs/news +https://crystalheavenjewellery.com/en-au/pages/newsletter +https://www.jewellerssupplies.com.au/ +https://www.herbcottage.com.au/pages/herb-lovers-community-newsletter +https://shop.thegrounds.com.au/ +https://www.vakantiestunt.nl/nieuwsbrief.php +https://www.vakantieveilingen.nl/nieuwsbrief.html +https://nl.secretescapes.com/aanbiedingen +https://www.lepetitmarche.com.au/newsletter +https://www.lalunapetcare.com.au/subscribe +https://larochere-na.com/pages/newsletter +https://store.latimes.com/ +https://www.amiparis.com/au/ +https://filterstore.com.au/pages/newsletter +https://www.versusmerch.com/pages/subscribe-to-our-newsletter +https://www.passionandpresence.com/newsletter-signup/ +https://www.lelo.com/blog/newsletter/ +https://www.tootsiescabaret.com/ +https://www.pimpbangkok.com/ +https://matadornetwork.com/ +https://pragueforadults.com/category/adult/strip-clubs/ +https://gayforgood.org/join-our-mailing-list/ +https://www.postcardmania.com/products-services/lgbt-mailing-list/ +https://gaygaddis.com/newsletter-subscribe/ +https://www.gayparentstobe.com/join-community +https://www.dunasmap.com/newsletter +http://www.pridecenterwny.org/Newsletter +https://www.lgbtgreat.com/newsletter +https://www.italygaytravels.com/newsletter/ +https://www.glbthistory.org/newsletter +https://www.chirale.it/iscriviti-alla-nostra-newsletter/ +https://www.ail.it/newsletter +https://www.fondazionebrodolini.it/iscriviti-alla-nostra-newsletter +https://sex-matters.org/take-action/sign-up/ +https://link.menshealth.com/join/5u0/thehookupsignup +https://whiskeydanduncensored.com/newsletter-socialmedia +https://rebelliousmagazine.com/sex-rebelle/ +https://allbodies.com/newsletter-sign-up/ +https://sexwithemily.com/subscribe/ +https://www.wonderlandbakery.com/ +https://www.carlybloombooks.com/the-bloomin-news/ +https://www.slaa.org.au/subscribe/ +https://www.onlyfriends.com.au/ +https://www.clubx.com.au/ +https://booko.com.au/ +https://sextoyserotica.com.au/ +https://hawttt.com.au/ +https://www.filmink.com.au/best-hookup-sites-australia-13-free-aussie-dating-apps/ +https://www.ricksnewyork.com/ +https://www.clubonyxcharlotte.com/ +https://www.goldclubcenterfolds.com/ +https://www.labohemegc.com/ +https://www.rickschicago.com/ +https://thewalrus.ca/welcome-to-the-manor-church-by-day-strip-club-by-night/ +https://vintagegaybooks.substack.com/ diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..4d24ccc --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2114 @@ +{ + "name": "sign_up_email", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sign_up_email", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@2captcha/captcha-solver": "^1.3.2", + "@iarna/toml": "^2.2.5", + "2captcha": "^3.0.5-2", + "axios": "^1.13.5", + "commander": "^14.0.3", + "playwright": "^1.56.1", + "playwright-extra": "^4.3.6", + "pngjs": "^7.0.0", + "puppeteer": "^24.30.0", + "puppeteer-extra": "^3.3.6", + "puppeteer-extra-plugin-stealth": "^2.11.2", + "tesseract.js": "^7.0.0" + } + }, + "node_modules/@2captcha/captcha-solver": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@2captcha/captcha-solver/-/captcha-solver-1.3.2.tgz", + "integrity": "sha512-tSxkzOZwgcGkIAOXXrfYGw6fSGk2WU/4StT91mjGfQZL891UEc5pbmGqrX/7hru3UqR1R+AyGwHs7g+QLU8/sg==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.1" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@iarna/toml": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@iarna/toml/-/toml-2.2.5.tgz", + "integrity": "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg==", + "license": "ISC" + }, + "node_modules/@puppeteer/browsers": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.12.1.tgz", + "integrity": "sha512-fXa6uXLxfslBlus3MEpW8S6S9fe5RwmAE5Gd8u3krqOwnkZJV3/lQJiY3LaFdTctLLqJtyMgEUGkbDnRNf6vbQ==", + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.4.3", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.5.0", + "semver": "^7.7.4", + "tar-fs": "^3.1.1", + "yargs": "^17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "license": "MIT" + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.2.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", + "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/2captcha": { + "version": "3.0.5-2", + "resolved": "https://registry.npmjs.org/2captcha/-/2captcha-3.0.5-2.tgz", + "integrity": "sha512-p/C1UFss2PvBxYjypuRZ4zE1n3C5VK7hnNijnTetF9sKg5iifF6xfos3h3CJQLGB//0Ksa1C77ItM3gOhHy7fA==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.1" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.13.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", + "integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/b4a": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.4.tgz", + "integrity": "sha512-u20zJLDaSWpxaZ+zaAkEIB2dZZ1o+DF4T/MRbmsvGp9nletHOyiai19OzX1fF8xUBYsO1bPXxODvcd0978pnug==", + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.4.tgz", + "integrity": "sha512-POK4oplfA7P7gqvetNmCs4CNtm9fNsx+IAh7jH7GgU0OJdge2rso0R20TNWVq6VoWcCvsTdlNDaleLHGaKx8CA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-os": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz", + "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "bare": ">=1.14.0" + } + }, + "node_modules/bare-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/bare-stream": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz", + "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "streamx": "^2.21.0" + }, + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.3.2.tgz", + "integrity": "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/basic-ftp": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.1.0.tgz", + "integrity": "sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/bmp-js": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz", + "integrity": "sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==", + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chromium-bidi": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-14.0.0.tgz", + "integrity": "sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw==", + "license": "Apache-2.0", + "dependencies": { + "mitt": "^3.0.1", + "zod": "^3.24.1" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone-deep": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz", + "integrity": "sha512-we+NuQo2DHhSl+DP6jlUiAhyAjBQrYnpOk15rN6c6JSPScjiCLh8IbSU+VTcph6YS3o7mASE8a0+gbZ7ChLpgg==", + "license": "MIT", + "dependencies": { + "for-own": "^0.1.3", + "is-plain-object": "^2.0.1", + "kind-of": "^3.0.2", + "lazy-cache": "^1.0.3", + "shallow-clone": "^0.1.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/devtools-protocol": { + "version": "0.0.1566079", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1566079.tgz", + "integrity": "sha512-MJfAEA1UfVhSs7fbSQOG4czavUp1ajfg6prlAN0+cmfa2zNjaIbvq8VneP7do1WAQQIvgNJWSMeP6UyI90gIlQ==", + "license": "BSD-3-Clause" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "license": "MIT" + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/for-own": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", + "integrity": "sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw==", + "license": "MIT", + "dependencies": { + "for-in": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-uri": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", + "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/idb-keyval": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.2.tgz", + "integrity": "sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==", + "license": "Apache-2.0" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "license": "MIT" + }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "license": "MIT" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "license": "MIT", + "dependencies": { + "is-buffer": "^1.1.5" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge-deep": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/merge-deep/-/merge-deep-3.0.3.tgz", + "integrity": "sha512-qtmzAS6t6grwEkNrunqTBdn0qKwFgNWvlxUbAV8es9M7Ot1EbyApytCnvE0jALPa46ZpKDUo527kKiaWplmlFA==", + "license": "MIT", + "dependencies": { + "arr-union": "^3.1.0", + "clone-deep": "^0.2.4", + "kind-of": "^3.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, + "node_modules/mixin-object": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz", + "integrity": "sha512-ALGF1Jt9ouehcaXaHhn6t1yGWRqGaHkPFndtFVHfZXOvkIZ/yoGaSi0AHVTafb3ZBGg4dr/bDwnaEKqCXzchMA==", + "license": "MIT", + "dependencies": { + "for-in": "^0.1.3", + "is-extendable": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/mixin-object/node_modules/for-in": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz", + "integrity": "sha512-F0to7vbBSHP8E3l6dCjxNOLuSFAACIxFy3UehTUlG7svlXi37HHsDkyVcHo0Pq8QwrE+pXvWSVX3ZT1T9wAZ9g==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "license": "MIT", + "bin": { + "opencollective-postinstall": "index.js" + } + }, + "node_modules/pac-proxy-agent": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/playwright": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz", + "integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==", + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.58.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.58.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", + "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright-extra": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/playwright-extra/-/playwright-extra-4.3.6.tgz", + "integrity": "sha512-q2rVtcE8V8K3vPVF1zny4pvwZveHLH8KBuVU2MoE3Jw4OKVoBWsHI9CH9zPydovHHOCDxjGN2Vg+2m644q3ijA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "playwright": "*", + "playwright-core": "*" + }, + "peerDependenciesMeta": { + "playwright": { + "optional": true + }, + "playwright-core": { + "optional": true + } + } + }, + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "license": "MIT", + "engines": { + "node": ">=14.19.0" + } + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/proxy-agent": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.6", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.1.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/puppeteer": { + "version": "24.37.3", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-24.37.3.tgz", + "integrity": "sha512-AUGGWq0BhPM+IOS2U9A+ZREH3HDFkV1Y5HERYGDg5cbGXjoGsTCT7/A6VZRfNU0UJJdCclyEimZICkZW6pqJyw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "2.12.1", + "chromium-bidi": "14.0.0", + "cosmiconfig": "^9.0.0", + "devtools-protocol": "0.0.1566079", + "puppeteer-core": "24.37.3", + "typed-query-selector": "^2.12.0" + }, + "bin": { + "puppeteer": "lib/cjs/puppeteer/node/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/puppeteer-core": { + "version": "24.37.3", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.37.3.tgz", + "integrity": "sha512-fokQ8gv+hNgsRWqVuP5rUjGp+wzV5aMTP3fcm8ekNabmLGlJdFHas1OdMscAH9Gzq4Qcf7cfI/Pe6wEcAqQhqg==", + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "2.12.1", + "chromium-bidi": "14.0.0", + "debug": "^4.4.3", + "devtools-protocol": "0.0.1566079", + "typed-query-selector": "^2.12.0", + "webdriver-bidi-protocol": "0.4.1", + "ws": "^8.19.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/puppeteer-extra": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/puppeteer-extra/-/puppeteer-extra-3.3.6.tgz", + "integrity": "sha512-rsLBE/6mMxAjlLd06LuGacrukP2bqbzKCLzV1vrhHFavqQE/taQ2UXv3H5P0Ls7nsrASa+6x3bDbXHpqMwq+7A==", + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.0", + "debug": "^4.1.1", + "deepmerge": "^4.2.2" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "@types/puppeteer": "*", + "puppeteer": "*", + "puppeteer-core": "*" + }, + "peerDependenciesMeta": { + "@types/puppeteer": { + "optional": true + }, + "puppeteer": { + "optional": true + }, + "puppeteer-core": { + "optional": true + } + } + }, + "node_modules/puppeteer-extra-plugin": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin/-/puppeteer-extra-plugin-3.2.3.tgz", + "integrity": "sha512-6RNy0e6pH8vaS3akPIKGg28xcryKscczt4wIl0ePciZENGE2yoaQJNd17UiEbdmh5/6WW6dPcfRWT9lxBwCi2Q==", + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.0", + "debug": "^4.1.1", + "merge-deep": "^3.0.1" + }, + "engines": { + "node": ">=9.11.2" + }, + "peerDependencies": { + "playwright-extra": "*", + "puppeteer-extra": "*" + }, + "peerDependenciesMeta": { + "playwright-extra": { + "optional": true + }, + "puppeteer-extra": { + "optional": true + } + } + }, + "node_modules/puppeteer-extra-plugin-stealth": { + "version": "2.11.2", + "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-stealth/-/puppeteer-extra-plugin-stealth-2.11.2.tgz", + "integrity": "sha512-bUemM5XmTj9i2ZerBzsk2AN5is0wHMNE6K0hXBzBXOzP5m5G3Wl0RHhiqKeHToe/uIH8AoZiGhc1tCkLZQPKTQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "puppeteer-extra-plugin": "^3.2.3", + "puppeteer-extra-plugin-user-preferences": "^2.4.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "playwright-extra": "*", + "puppeteer-extra": "*" + }, + "peerDependenciesMeta": { + "playwright-extra": { + "optional": true + }, + "puppeteer-extra": { + "optional": true + } + } + }, + "node_modules/puppeteer-extra-plugin-user-data-dir": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-user-data-dir/-/puppeteer-extra-plugin-user-data-dir-2.4.1.tgz", + "integrity": "sha512-kH1GnCcqEDoBXO7epAse4TBPJh9tEpVEK/vkedKfjOVOhZAvLkHGc9swMs5ChrJbRnf8Hdpug6TJlEuimXNQ+g==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^10.0.0", + "puppeteer-extra-plugin": "^3.2.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "playwright-extra": "*", + "puppeteer-extra": "*" + }, + "peerDependenciesMeta": { + "playwright-extra": { + "optional": true + }, + "puppeteer-extra": { + "optional": true + } + } + }, + "node_modules/puppeteer-extra-plugin-user-preferences": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-user-preferences/-/puppeteer-extra-plugin-user-preferences-2.4.1.tgz", + "integrity": "sha512-i1oAZxRbc1bk8MZufKCruCEC3CCafO9RKMkkodZltI4OqibLFXF3tj6HZ4LZ9C5vCXZjYcDWazgtY69mnmrQ9A==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "deepmerge": "^4.2.2", + "puppeteer-extra-plugin": "^3.2.3", + "puppeteer-extra-plugin-user-data-dir": "^2.4.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "playwright-extra": "*", + "puppeteer-extra": "*" + }, + "peerDependenciesMeta": { + "playwright-extra": { + "optional": true + }, + "puppeteer-extra": { + "optional": true + } + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shallow-clone": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz", + "integrity": "sha512-J1zdXCky5GmNnuauESROVu31MQSnLoYvlyEn6j2Ztk6Q5EHFIhxkMhYcv6vuDzl2XEzoRr856QwzMgWM/TmZgw==", + "license": "MIT", + "dependencies": { + "is-extendable": "^0.1.1", + "kind-of": "^2.0.1", + "lazy-cache": "^0.2.3", + "mixin-object": "^2.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shallow-clone/node_modules/kind-of": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", + "integrity": "sha512-0u8i1NZ/mg0b+W3MGGw5I7+6Eib2nx72S/QvXa0hYjEkjTknYmEYQJwGu3mLC0BrhtJjtQafTkyRUQ75Kx0LVg==", + "license": "MIT", + "dependencies": { + "is-buffer": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shallow-clone/node_modules/lazy-cache": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz", + "integrity": "sha512-gkX52wvU/R8DVMMt78ATVPFMJqfW8FPz1GZ1sVHBVQHmu/WvhIWE4cE1GBzhJNFicDeYhnwp6Rl35BcAIM3YOQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/streamx": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar-fs": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz", + "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==", + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/tar-stream": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", + "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/tesseract.js": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tesseract.js/-/tesseract.js-7.0.0.tgz", + "integrity": "sha512-exPBkd+z+wM1BuMkx/Bjv43OeLBxhL5kKWsz/9JY+DXcXdiBjiAch0V49QR3oAJqCaL5qURE0vx9Eo+G5YE7mA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "bmp-js": "^0.1.0", + "idb-keyval": "^6.2.0", + "is-url": "^1.2.4", + "node-fetch": "^2.6.9", + "opencollective-postinstall": "^2.0.3", + "regenerator-runtime": "^0.13.3", + "tesseract.js-core": "^7.0.0", + "wasm-feature-detect": "^1.8.0", + "zlibjs": "^0.3.1" + } + }, + "node_modules/tesseract.js-core": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/tesseract.js-core/-/tesseract.js-core-7.0.0.tgz", + "integrity": "sha512-WnNH518NzmbSq9zgTPeoF8c+xmilS8rFIl1YKbk/ptuuc7p6cLNELNuPAzcmsYw450ca6bLa8j3t0VAtq435Vw==", + "license": "Apache-2.0" + }, + "node_modules/text-decoder": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.6.tgz", + "integrity": "sha512-27FeW5GQFDfw0FpwMQhMagB7BztOOlmjcSRi97t2oplhKVTZtp0DZbSegSaXS5IIC6mxMvBG4AR1Sgc6BX3CQg==", + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/typed-query-selector": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz", + "integrity": "sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT", + "optional": true + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/wasm-feature-detect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz", + "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==", + "license": "Apache-2.0" + }, + "node_modules/webdriver-bidi-protocol": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.1.tgz", + "integrity": "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==", + "license": "Apache-2.0" + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/zlibjs": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz", + "integrity": "sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..99e61ef --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "sign_up_email", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "type": "commonjs", + "dependencies": { + "@2captcha/captcha-solver": "^1.3.2", + "@iarna/toml": "^2.2.5", + "2captcha": "^3.0.5-2", + "axios": "^1.13.5", + "commander": "^14.0.3", + "playwright": "^1.56.1", + "playwright-extra": "^4.3.6", + "pngjs": "^7.0.0", + "puppeteer": "^24.30.0", + "puppeteer-extra": "^3.3.6", + "puppeteer-extra-plugin-stealth": "^2.11.2", + "tesseract.js": "^7.0.0" + } +} diff --git a/site_results.txt b/site_results.txt new file mode 100644 index 0000000..7787c28 --- /dev/null +++ b/site_results.txt @@ -0,0 +1,87 @@ +DATE | TIME | STATUS | WEBSITE | RESULT +-------------------------------------------------------------------------------------------------------------- +15/02/2026 | 12:55:22 | SUCCESS | Dutch News Test (https://www.dut | Success +15/02/2026 | 12:55:27 | SUCCESS | Immaculate Vegan (https://immacu | Success +15/02/2026 | 12:55:30 | FAILED | Gate6 (https://gate6.vn/) | Navigating frame was detached +15/02/2026 | 12:55:31 | FAILED | Kulala Land (https://kulalaland. | Connection closed. +15/02/2026 | 12:56:00 | SUCCESS | Immaculate Vegan (https://immacu | Success +15/02/2026 | 13:05:01 | FAILED | Immaculate Vegan (https://immacu | hCaptcha solving failed +15/02/2026 | 13:07:45 | FAILED | Immaculate Vegan (https://immacu | hCaptcha solving failed +15/02/2026 | 13:10:49 | FAILED | Immaculate Vegan (https://immacu | hCaptcha solving failed +15/02/2026 | 13:18:48 | FAILED | Immaculate Vegan (https://immacu | hCaptcha solving failed +15/02/2026 | 13:25:45 | FAILED | Site_1 (https://immaculatevegan. | hCaptcha solving failed +15/02/2026 | 13:25:52 | SUCCESS | Site_2 (https://gate6.vn/) | Success +15/02/2026 | 13:26:27 | SUCCESS | Site_3 (https://www.sprintzeal.c | Success +15/02/2026 | 13:26:30 | FAILED | Site_4 (https://kulalaland.com/) | No email field detected +15/02/2026 | 13:26:35 | SUCCESS | Site_5 (https://www.goplaces.co. | Success +15/02/2026 | 13:26:43 | SUCCESS | Site_6 (https://www.fiftyfiftybo | Success +15/02/2026 | 13:26:47 | FAILED | Site_7 (https://jubina.com/blog) | No email field detected +15/02/2026 | 13:26:53 | FAILED | Site_8 (https://www.sneakywholef | Not a newsletter signup form +15/02/2026 | 13:26:58 | SUCCESS | Site_9 (https://vegnews.com/sign | Success +15/02/2026 | 13:27:04 | SUCCESS | Site_10 (https://yonayonabeerwor | Success +15/02/2026 | 13:27:21 | SUCCESS | Site_11 (https://www.louispowers | Success +15/02/2026 | 13:27:28 | SUCCESS | Site_12 (https://www.hairworthy. | Success +15/02/2026 | 13:27:34 | FAILED | Site_13 (https://endotaspa.com.a | Not a newsletter signup form +15/02/2026 | 13:27:39 | SUCCESS | Site_14 (https://prestigify.com/ | Success +15/02/2026 | 13:27:42 | FAILED | Site_15 (https://nobones.co/) | No email field detected +15/02/2026 | 13:28:01 | SUCCESS | Site_16 (https://www.sofamania.c | Success +15/02/2026 | 13:28:12 | FAILED | Site_17 (https://dimoradellebalz | CAPTCHA present: reCAPTCHA +15/02/2026 | 13:28:25 | SUCCESS | Site_18 (https://www.42photo.com | Success +15/02/2026 | 13:28:27 | FAILED | Site_19 (https://ifinishedmybase | Execution context was destroyed, most likely because of a navigation. +15/02/2026 | 13:28:30 | FAILED | Site_20 (https://tettixgames.com | Navigating frame was detached +15/02/2026 | 13:28:31 | FAILED | Site_21 (https://www.corinnekai. | Connection closed. +15/02/2026 | 13:29:19 | FAILED | Site_1 (https://immaculatevegan. | hCaptcha solving failed +15/02/2026 | 13:29:25 | SUCCESS | Site_2 (https://gate6.vn/) | Success +15/02/2026 | 13:29:31 | SUCCESS | Site_3 (https://www.sprintzeal.c | Success +15/02/2026 | 13:29:45 | FAILED | Site_4 (https://kulalaland.com/) | No email field detected +15/02/2026 | 13:29:50 | SUCCESS | Site_5 (https://www.goplaces.co. | Success +15/02/2026 | 13:30:06 | SUCCESS | Site_6 (https://www.fiftyfiftybo | Success +15/02/2026 | 13:30:09 | FAILED | Site_7 (https://jubina.com/blog) | No email field detected +15/02/2026 | 13:30:27 | FAILED | Site_8 (https://www.sneakywholef | Not a newsletter signup form +15/02/2026 | 13:30:38 | SUCCESS | Site_9 (https://vegnews.com/sign | Success +15/02/2026 | 13:30:43 | SUCCESS | Site_10 (https://yonayonabeerwor | Success +15/02/2026 | 13:30:47 | SUCCESS | Site_11 (https://www.louispowers | Success +15/02/2026 | 13:30:57 | SUCCESS | Site_12 (https://www.hairworthy. | Success +15/02/2026 | 13:31:03 | FAILED | Site_13 (https://endotaspa.com.a | Not a newsletter signup form +15/02/2026 | 13:31:14 | SUCCESS | Site_14 (https://prestigify.com/ | Success +15/02/2026 | 13:31:16 | FAILED | Site_15 (https://nobones.co/) | No email field detected +15/02/2026 | 13:31:22 | SUCCESS | Site_16 (https://www.sofamania.c | Success +15/02/2026 | 13:31:29 | FAILED | Site_17 (https://dimoradellebalz | CAPTCHA present: reCAPTCHA +15/02/2026 | 13:31:34 | SUCCESS | Site_18 (https://www.42photo.com | Success +15/02/2026 | 13:31:37 | FAILED | Site_19 (https://ifinishedmybase | Execution context was destroyed, most likely because of a navigation. +15/02/2026 | 13:31:41 | SUCCESS | Site_20 (https://tettixgames.com | Success +15/02/2026 | 13:31:48 | SUCCESS | Site_21 (https://www.corinnekai. | Success +15/02/2026 | 13:31:59 | SUCCESS | Site_22 (https://www.blendnwhip. | Success +15/02/2026 | 13:32:04 | FAILED | Site_23 (https://www.brookebowli | Submit failed: SyntaxError: Failed to execute 'querySelector' on 'Document': '. +' is not a valid selector. +15/02/2026 | 13:32:07 | FAILED | Site_24 (https://www.thatveganda | Submit failed: SyntaxError: Failed to execute 'querySelector' on 'Document': '. +' is not a valid selector. +15/02/2026 | 13:32:14 | SUCCESS | Site_25 (https://comhwa.org.au/b | Success +15/02/2026 | 13:32:18 | FAILED | Site_26 (https://hiutdenim.co.uk | Submit failed: Node is either not clickable or not an Element +15/02/2026 | 13:32:24 | SUCCESS | Site_27 (https://local-lovely.co | Success +15/02/2026 | 13:32:40 | FAILED | Site_28 (https://vituswholefoods | Submit failed: No element found for selector: button:nth-of-type(0) +15/02/2026 | 13:32:45 | SUCCESS | Site_29 (https://theoriginalshab | Success +15/02/2026 | 13:32:52 | FAILED | Site_30 (https://rothys.com/en-a | No email field detected +15/02/2026 | 13:32:59 | FAILED | Site_31 (https://www.trustbasket | Not a newsletter signup form +15/02/2026 | 13:33:02 | FAILED | Site_32 (https://chelseabrice.co | Submit failed: SyntaxError: Failed to execute 'querySelector' on 'Document': '. +' is not a valid selector. +15/02/2026 | 13:33:06 | SUCCESS | Site_33 (https://wolfsgamingblog | Success +15/02/2026 | 13:33:11 | FAILED | Site_34 (https://www.leadfuze.co | No email field detected +15/02/2026 | 13:33:16 | FAILED | Site_35 (https://thedefiant.io/) | No email field detected +15/02/2026 | 13:33:22 | FAILED | Site_36 (https://www.entrepreneu | No email field detected +15/02/2026 | 13:33:32 | SUCCESS | Site_37 (https://pantproject.com | Success +15/02/2026 | 13:33:38 | FAILED | Site_38 (https://www.terrebleu.c | No email field detected +15/02/2026 | 13:33:41 | FAILED | Site_39 (https://www.shopify.com | Failed to fill email: SyntaxError: Failed to execute 'querySelector' on 'Document': '#email-input-:Rdbh4ll5H1:' is not a valid selector. +15/02/2026 | 13:33:47 | SUCCESS | Site_40 (https://www.skooob.com/ | Success +15/02/2026 | 13:33:52 | SUCCESS | Site_41 (https://mremountain.com | Success +15/02/2026 | 13:34:06 | SUCCESS | Site_42 (https://uncommonjames.c | Success +15/02/2026 | 13:34:11 | FAILED | Site_43 (https://www.axios.com/s | CAPTCHA present: reCAPTCHA +15/02/2026 | 13:34:14 | FAILED | Site_44 (https://www.dietassassi | Submit failed: SyntaxError: Failed to execute 'querySelector' on 'Document': '. +' is not a valid selector. +15/02/2026 | 13:34:19 | SUCCESS | Site_45 (https://miriamporter.ca | Success +15/02/2026 | 13:34:23 | FAILED | Site_46 (https://uppercasemagazi | No email field detected +15/02/2026 | 13:34:29 | SUCCESS | Site_47 (https://www.mercadodasr | Success +15/02/2026 | 13:34:35 | SUCCESS | Site_48 (https://mybageecha.com/ | Success +15/02/2026 | 13:34:48 | FAILED | Site_49 (https://bebemoss.com/) | Submit failed: Node is either not clickable or not an Element +15/02/2026 | 13:34:50 | FAILED | Site_50 (https://linkwithin.com/ | net::ERR_CONNECTION_RESET at https://linkwithin.com/ +15/02/2026 | 13:43:01 | FAILED | Immaculate Vegan (https://immacu | Navigating frame was detached diff --git a/src/captcha.js b/src/captcha.js new file mode 100644 index 0000000..474b5da --- /dev/null +++ b/src/captcha.js @@ -0,0 +1,471 @@ +const Tesseract = require('tesseract.js'); +const https = require('https'); +const http = require('http'); + +const { Solver } = require('@2captcha/captcha-solver'); +const solver = new Solver('9ed0ef51badf9a017ac50aea413d8001'); + +const CAPTCHA_PATTERNS = { + recaptcha: { + selectors: ['.g-recaptcha', '[data-sitekey]', 'iframe[src*="recaptcha"]', '#recaptcha'], + name: 'reCAPTCHA' + }, + hcaptcha: { + selectors: ['.h-captcha', '[data-hcaptcha-sitekey]', 'iframe[src*="hcaptcha"]', '#hcaptcha', '[data-sitekey*="hcaptcha"]', '.hcaptcha-checkbox'], + name: 'hCaptcha' + }, + textCaptcha: { + selectors: ['input[name*="captcha"]', 'input[id*="captcha"]', '.captcha-input', '#captcha'], + name: 'Text CAPTCHA' + }, + imageCaptcha: { + selectors: ['img[src*="captcha"]', '.captcha-image', '#captcha-image', 'img[alt*="captcha" i]'], + name: 'Image CAPTCHA' + }, + mathCaptcha: { + selectors: ['.math-captcha', '[name*="math"]', 'input[placeholder*="math" i]'], + name: 'Math CAPTCHA' + } +}; + +function fetchImageAsBuffer(url) { + return new Promise((resolve, reject) => { + const client = url.startsWith('https') ? https : http; + client.get(url, (res) => { + const chunks = []; + res.on('data', (chunk) => chunks.push(chunk)); + res.on('end', () => resolve(Buffer.concat(chunks))); + res.on('error', reject); + }).on('error', reject); + }); +} + +async function detectCaptcha(page) { + const captchaInfo = await page.evaluate((patterns) => { + const results = []; + + for (const [type, config] of Object.entries(patterns)) { + for (const selector of config.selectors) { + const elements = document.querySelectorAll(selector); + if (elements.length > 0) { + for (const el of elements) { + const style = window.getComputedStyle(el); + const rect = el.getBoundingClientRect(); + const isVisible = style.display !== 'none' && + style.visibility !== 'hidden' && + style.opacity !== '0' && + rect.width > 0 && + rect.height > 0; + + results.push({ + type: type, + name: config.name, + selector: selector, + count: elements.length, + isVisible: isVisible + }); + } + break; + } + } + } + + return results; + }, CAPTCHA_PATTERNS); + + return captchaInfo; +} + +async function solveSimpleCaptcha(page) { + const mathSolution = await page.evaluate(() => { + const mathText = document.querySelector('.math-captcha, .captcha-question, [class*="math"]'); + if (mathText) { + const text = mathText.textContent || ''; + const match = text.match(/(\d+)\s*([+\-*/])\s*(\d+)/); + if (match) { + const [_, a, op, b] = match; + let result; + switch(op) { + case '+': result = parseInt(a) + parseInt(b); break; + case '-': result = parseInt(a) - parseInt(b); break; + case '*': result = parseInt(a) * parseInt(b); break; + case '/': result = Math.floor(parseInt(a) / parseInt(b)); break; + } + return { type: 'math', answer: result, text: text }; + } + } + return null; + }); + + if (mathSolution) { + return mathSolution; + } + + const imageCaptchaInfo = await page.evaluate(() => { + const selectors = [ + 'img[src*="captcha"]', + '.captcha-image img', + '#captcha-image', + 'img[alt*="captcha" i]', + 'img[class*="captcha" i]' + ]; + + for (const selector of selectors) { + const img = document.querySelector(selector); + if (img && img.src) { + return { src: img.src, selector: selector }; + } + } + return null; + }); + + if (imageCaptchaInfo) { + console.log(' Attempting 2Captcha image solving...'); + console.log(` CAPTCHA image selector: ${imageCaptchaInfo.selector}`); + + // Wait for CAPTCHA image to fully load before capturing + console.log(' Waiting for CAPTCHA image to load...'); + let imageLoaded = false; + const maxWaitMs = 5000; + const checkIntervalMs = 200; + let waitedMs = 0; + + while (!imageLoaded && waitedMs < maxWaitMs) { + imageLoaded = await page.evaluate((selector) => { + const el = document.querySelector(selector); + return el && el.naturalWidth > 0 && el.naturalHeight > 0; + }, imageCaptchaInfo.selector).catch(() => false); + + if (!imageLoaded) { + await new Promise(r => setTimeout(r, checkIntervalMs)); + waitedMs += checkIntervalMs; + } + } + + if (imageLoaded) { + console.log(` CAPTCHA image loaded successfully (waited ${waitedMs}ms)`); + } else { + console.log(' WARNING: CAPTCHA may not have fully loaded, proceeding anyway...'); + } + + // Additional small delay to ensure rendering is complete + await new Promise(r => setTimeout(r, 300)); + + try { + let imageData; + + // Get CAPTCHA element bounding box + const captchaInfo = await page.evaluate((selector) => { + const img = document.querySelector(selector); + if (!img) return null; + const rect = img.getBoundingClientRect(); + return { + src: img.src, + x: rect.x, + y: rect.y, + width: rect.width, + height: rect.height + }; + }, imageCaptchaInfo.selector); + + if (captchaInfo && captchaInfo.width > 0) { + console.log(' CAPTCHA at:', captchaInfo.x, captchaInfo.y, captchaInfo.width, 'x', captchaInfo.height); + + // Try to get element screenshot directly + try { + const captchaElement = await page.$(imageCaptchaInfo.selector); + if (captchaElement) { + const elementScreenshot = await captchaElement.screenshot({ type: 'png' }); + if (Buffer.isBuffer(elementScreenshot)) { + imageData = elementScreenshot.toString('base64'); + } else { + imageData = Buffer.from(elementScreenshot).toString('base64'); + } + console.log(' Element screenshot captured, size:', imageData.length, 'bytes (base64)'); + } + } catch (elemError) { + console.log(' Element screenshot failed:', elemError.message); + + // Fallback: capture full page and crop + const fullScreenshot = await page.screenshot({ type: 'png', encoding: 'binary' }); + const screenshotBuffer = Buffer.from(fullScreenshot, 'binary'); + const png = require('pngjs').PNG.sync.read(screenshotBuffer); + + const cropX = Math.max(0, Math.floor(captchaInfo.x)); + const cropY = Math.max(0, Math.floor(captchaInfo.y)); + const cropW = Math.floor(captchaInfo.width); + const cropH = Math.floor(captchaInfo.height); + + console.log(' Cropping to:', cropX, cropY, cropW, cropH); + + const cropped = new require('pngjs').PNG({ width: cropW, height: cropH }); + + for (let y = 0; y < cropH; y++) { + for (let x = 0; x < cropW; x++) { + const srcIdx = ((cropY + y) * png.width + (cropX + x)) * 4; + const dstIdx = (y * cropW + x) * 4; + cropped.data[dstIdx] = png.data[srcIdx]; + cropped.data[dstIdx + 1] = png.data[srcIdx + 1]; + cropped.data[dstIdx + 2] = png.data[srcIdx + 2]; + cropped.data[dstIdx + 3] = png.data[srcIdx + 3]; + } + } + + imageData = require('pngjs').PNG.sync.write(cropped).toString('base64'); + console.log(' Cropped image size:', imageData.length, 'bytes (base64)'); + } + } else if (captchaInfo && imageCaptchaInfo.src.startsWith('data:')) { + const base64 = imageCaptchaInfo.src.replace(/^data:image\/\w+;base64,/, ''); + imageData = base64; + } + + console.log(' Sending to 2Captcha (human workers)...'); + const result = await solver.imageCaptcha({ + body: imageData, + numeric: 0, + min_len: 1, + max_len: 10, + phrase: 0, + caseSensitive: 1, + calc: 0, + lang: 'en' + }); + + console.log(` 2Captcha solved: "${result}"`); + return { + type: 'image', + answer: result.trim(), + originalText: result, + service: '2captcha', + imageSrc: imageCaptchaInfo.src + }; + + } catch (error) { + console.log(` 2Captcha failed: ${error.message}`); + + console.log(' Falling back to Tesseract OCR...'); + try { + const { data: { text, confidence } } = await Tesseract.recognize( + imageCaptchaInfo.src, + 'eng', + { logger: m => {} } + ); + + console.log(` OCR raw output: "${text}"`); + console.log(` OCR confidence: ${confidence}%`); + + const cleanedText = text.replace(/[^a-zA-Z0-9]/g, '').trim(); + + if (cleanedText && cleanedText.length >= 3) { + console.log(` OCR fallback read: "${cleanedText}"`); + return { + type: 'image', + answer: cleanedText, + originalText: text, + confidence: confidence, + service: 'tesseract-fallback', + imageSrc: imageCaptchaInfo.src + }; + } + } catch (ocrError) { + console.log(` OCR fallback also failed: ${ocrError.message}`); + } + + return { + type: 'image', + answer: null, + message: `Both 2Captcha and OCR failed: ${error.message}`, + imageSrc: imageCaptchaInfo.src + }; + } + } + + return null; +} + +async function solveHCaptcha(page, pageUrl) { + try { + // First, try to find and click the hCaptcha checkbox directly via JavaScript + console.log(` Attempting to click hCaptcha checkbox directly...`); + + const clickResult = await page.evaluate(() => { + // Try multiple approaches to check the checkbox + const selectors = [ + '#checkbox', + '.hcaptcha-checkbox', + '[class*="hcaptcha"]', + '[role="checkbox"]', + 'div[aria-haspopup="true"]' + ]; + + for (const selector of selectors) { + const el = document.querySelector(selector); + if (el) { + // Try clicking it + el.click(); + + // Also try setting aria-checked + el.setAttribute('aria-checked', 'true'); + + // Check if it has a checked state now + const checked = el.getAttribute('aria-checked'); + return { clicked: true, selector: selector, ariaChecked: checked }; + } + } + return { clicked: false }; + }); + + console.log(` Click result:`, clickResult); + + // Wait a moment and check if hCaptcha is now checked + await new Promise(r => setTimeout(r, 1500)); + + const hCaptchaChecked = await page.evaluate(() => { + const checkbox = document.querySelector('#checkbox, .hcaptcha-checkbox, [role="checkbox"]'); + if (checkbox) { + return checkbox.getAttribute('aria-checked') === 'true'; + } + return false; + }); + + if (hCaptchaChecked) { + console.log(` hCaptcha checkbox checked successfully!`); + return { type: 'hcaptcha', answer: 'clicked', service: 'direct-click' }; + } + + // If direct click didn't work, try 2Captcha API + console.log(` Direct click didn't work, trying 2Captcha API...`); + const hcaptchaInfo = await page.evaluate(() => { + // Look for sitekey in data attributes + const dataSelectors = [ + '[data-hcaptcha-sitekey]', + '[data-sitekey]', + '.h-captcha', + '#hcaptcha', + '[class*="hcaptcha"]' + ]; + + for (const selector of dataSelectors) { + const elements = document.querySelectorAll(selector); + for (const el of elements) { + const sitekey = el.getAttribute('data-hcaptcha-sitekey') || el.getAttribute('data-sitekey'); + if (sitekey && sitekey.length > 10) { + return { sitekey: sitekey, selector: selector }; + } + // Check parent chain + let parent = el.parentElement; + for (let i = 0; i < 5 && parent; i++) { + const parentKey = parent.getAttribute('data-hcaptcha-sitekey') || parent.getAttribute('data-sitekey'); + if (parentKey && parentKey.length > 10) { + return { sitekey: parentKey, selector: selector }; + } + parent = parent.parentElement; + } + } + } + + // Look for hCaptcha iframe and get sitekey from URL + const iframe = document.querySelector('iframe[src*="hcaptcha"]'); + if (iframe) { + const src = iframe.getAttribute('src') || ''; + const match = src.match(/sitekey=([^&]+)/); + if (match && match[1]) { + return { sitekey: match[1], selector: 'iframe[src*="hcaptcha"]' }; + } + } + + // Fallback: search entire HTML for hCaptcha sitekey pattern + const html = document.documentElement.innerHTML; + const sitekeyMatch = html.match(/["']sitekey["']\s*[:=]\s*["']([a-zA-Z0-9_-]{20,})["']/); + if (sitekeyMatch && sitekeyMatch[1]) { + return { sitekey: sitekeyMatch[1], selector: 'html-content' }; + } + + // Look for hcap sitekey + const hcapMatch = html.match(/hcap[tT]cha.*?["']sitekey["']\s*[:=]\s*["']([a-zA-Z0-9_-]{20,})["']/); + if (hcapMatch && hcapMatch[1]) { + return { sitekey: hcapMatch[1], selector: 'html-script' }; + } + + return null; + }); + + if (!hcaptchaInfo) { + console.log(` hCaptcha not found on page`); + return null; + } + + console.log(` Found hCaptcha sitekey: ${hcaptchaInfo.sitekey}`); + console.log(` Submitting to 2Captcha...`); + + // Use new @2captcha/captcha-solver package with object syntax + const result = await solver.hcaptcha({ + pageurl: pageUrl, + sitekey: hcaptchaInfo.sitekey + }); + + console.log(` hCaptcha solved: ${result}`); + + // Try multiple methods to set the token + await page.evaluate((token) => { + // Method 1: Find textarea + const textarea = document.querySelector('textarea[name="h-captcha-response"]'); + if (textarea) { + textarea.value = token; + textarea.dispatchEvent(new Event('input', { bubbles: true })); + textarea.dispatchEvent(new Event('change', { bubbles: true })); + console.log(' Set token in textarea'); + } + + // Method 2: Find iframe and set directly + const iframe = document.querySelector('iframe[src*="hcaptcha"]'); + if (iframe) { + try { + const iframeDoc = iframe.contentDocument || iframe.contentWindow.document; + const iframeTextarea = iframeDoc.querySelector('textarea[name="h-captcha-response"]'); + if (iframeTextarea) { + iframeTextarea.value = token; + iframeTextarea.dispatchEvent(new Event('input', { bubbles: true })); + console.log(' Set token in iframe textarea'); + } + } catch(e) { + console.log(' Cannot access iframe directly'); + } + } + + // Method 3: Set on any hCaptcha related element + document.querySelectorAll('[id*="hcaptcha"], [class*="hcaptcha"]').forEach(el => { + if (el.value !== undefined) el.value = token; + if (el.dataset.response) el.dataset.response = token; + }); + }, result.data); + + return { + type: 'hcaptcha', + answer: result.data, + service: '2captcha' + }; + } catch (error) { + const errorMsg = error.message || ''; + + if (errorMsg.includes('ERROR_METHOD_CALL') || errorMsg.includes('Unexpected Error')) { + console.log(` ERROR: hCaptcha not supported by your 2Captcha API key`); + console.log(` Your API key may not include hCaptcha solving.`); + console.log(` Options:`); + console.log(` - Contact 2Captcha to add hCaptcha to your plan`); + console.log(` - Use an alternative service like CapSolver or Anti-Captcha`); + } else { + console.log(` hCaptcha solving failed: ${errorMsg}`); + } + + return null; + } +} + +module.exports = { + CAPTCHA_PATTERNS, + detectCaptcha, + solveSimpleCaptcha, + solveHCaptcha, + fetchImageAsBuffer +}; diff --git a/src/classifier.js b/src/classifier.js new file mode 100644 index 0000000..652317b --- /dev/null +++ b/src/classifier.js @@ -0,0 +1,109 @@ +async function classifyNewsletterForm(page) { + const formInfo = await page.evaluate(() => { + const results = { + hasEmailInput: false, + hasNewsletterKeywords: false, + hasSearchInput: false, + hasLoginInput: false, + hasContactForm: false, + hasOnlyEmail: false, + formCount: 0, + emailInputCount: 0, + totalInputCount: 0, + confidence: 0, + reasons: [], + signals: { + positive: [], + negative: [] + } + }; + + const allInputs = document.querySelectorAll('input:not([type="hidden"]):not([type="submit"]):not([type="button"])'); + results.totalInputCount = allInputs.length; + + const emailInputs = document.querySelectorAll('input[type="email"], input[name*="email" i], input[id*="email" i], input[placeholder*="email" i]'); + results.emailInputCount = emailInputs.length; + results.hasEmailInput = emailInputs.length > 0; + if (results.hasEmailInput) { + results.signals.positive.push(`Found ${emailInputs.length} email input(s)`); + } + + const pageText = document.body.innerText.toLowerCase(); + const titleText = document.title.toLowerCase(); + const combinedText = pageText + ' ' + titleText; + + const newsletterKeywords = [ + 'newsletter', 'subscribe', 'signup', 'sign up', 'join our', 'stay updated', + 'get updates', 'email updates', 'weekly digest', 'monthly newsletter', + 'sign up for updates', 'join the list', 'email list', 'mailing list' + ]; + + for (const keyword of newsletterKeywords) { + if (combinedText.includes(keyword)) { + results.hasNewsletterKeywords = true; + results.signals.positive.push(`Keyword: "${keyword}"`); + } + } + + const searchInputs = document.querySelectorAll('input[type="search"], input[name*="search" i], input[id*="search" i], input[placeholder*="search" i]'); + results.hasSearchInput = searchInputs.length > 0; + if (results.hasSearchInput) { + results.signals.negative.push('Search input detected'); + results.reasons.push('Search input detected'); + } + + const passwordInputs = document.querySelectorAll('input[type="password"]'); + const usernameInputs = document.querySelectorAll('input[name*="username" i], input[name*="login" i], input[id*="username" i]'); + results.hasLoginInput = passwordInputs.length > 0 || usernameInputs.length > 0; + if (results.hasLoginInput) { + results.signals.negative.push('Login/password fields detected'); + results.reasons.push('Login/password fields detected'); + } + + const contactKeywords = ['contact us', 'get in touch', 'send message', 'inquiry']; + for (const keyword of contactKeywords) { + if (combinedText.includes(keyword)) { + results.hasContactForm = true; + results.signals.negative.push(`Contact form keyword: "${keyword}"`); + break; + } + } + + const nonEmailInputs = Array.from(allInputs).filter(input => { + const type = input.type?.toLowerCase(); + const name = input.name?.toLowerCase() || ''; + return type !== 'email' && !name.includes('email'); + }); + + results.hasOnlyEmail = results.hasEmailInput && nonEmailInputs.length === 0; + if (results.hasOnlyEmail) { + results.signals.positive.push('Only email field (no other inputs)'); + } + + let score = 0; + + if (results.hasEmailInput) score += 40; + if (results.hasNewsletterKeywords) score += 35; + if (results.hasOnlyEmail) score += 15; + if (results.totalInputCount <= 3) score += 10; + + if (!results.hasSearchInput) score += 10; + if (!results.hasLoginInput) score += 15; + if (!results.hasContactForm) score += 5; + + if (results.hasSearchInput) score -= 30; + if (results.hasLoginInput) score -= 50; + if (results.hasContactForm && !results.hasNewsletterKeywords) score -= 20; + + results.confidence = Math.max(0, Math.min(100, score)); + results.isNewsletter = results.confidence >= 55; + + return results; + }); + + return formInfo; +} + +module.exports = { + classifyNewsletterForm +}; diff --git a/src/cli.js b/src/cli.js new file mode 100644 index 0000000..0c18d77 --- /dev/null +++ b/src/cli.js @@ -0,0 +1,73 @@ +const { Command } = require('commander'); + +function createCLI() { + const program = new Command(); + + program + .name('newsletter-signup') + .description('Newsletter Signup Automation Tool') + .version('1.0.0') + .option('-f, --file ', 'Load sites from text file (one URL per line)') + .option('-k, --keep-open', 'Keep browser tabs open after processing for verification') + .option('-l, --log', 'Save output to timestamped log file') + .option('-d, --dry-run', 'Test mode - detect CAPTCHA but skip solving (no API calls)') + .option('-h, --help', 'Show help message') + .argument('[target]', 'Site name or URL to process (filters from config.toml sites)'); + + program.on('option:help', () => { + console.log(` + Newsletter Signup Automation Tool + + Usage: node index.js [options] [site-name-or-url] + + Options: + -f, --file= Load sites from text file (one URL per line) + -k, --keep-open Keep browser tabs open after processing for verification + -l, --log Save output to timestamped log file + -d, --dry-run Test mode - detect CAPTCHA but skip solving (no API calls) + -h, --help Show this help message + + Examples: + node index.js Process all sites from config.toml + node index.js --file=sites.txt Process sites from file + node index.js "Dutch News" Process specific site + node index.js --file=sites.txt --keep-open Keep tabs open for verification + node index.js --log Save output to log file + node index.js --dry-run Test without calling 2Captcha API + + Text file format (sites.txt): + # Lines starting with # are comments + https://example.com/newsletter + https://another-site.com/signup + + Configuration: + Edit config.toml to set credentials and default sites + `); + process.exit(0); + }); + + return program; +} + +function parseCLI(args = process.argv.slice(2)) { + const program = createCLI(); + + const fullArgs = ['node', 'index.js', ...args]; + program.parse(fullArgs); + + const opts = program.opts(); + + return { + target: program.args[0] || null, + file: opts.file || null, + keepOpen: opts.keepOpen || false, + log: opts.log || false, + dryRun: opts.dryRun || false, + help: opts.help || false + }; +} + +module.exports = { + createCLI, + parseCLI +}; diff --git a/src/config.js b/src/config.js new file mode 100644 index 0000000..ccc01d5 --- /dev/null +++ b/src/config.js @@ -0,0 +1,80 @@ +const fs = require('fs'); +const path = require('path'); +const toml = require('@iarna/toml'); + +function loadConfig() { + try { + const configPath = path.join(__dirname, '..', 'config.toml'); + const tomlString = fs.readFileSync(configPath, 'utf-8'); + const config = toml.parse(tomlString); + console.log('Configuration loaded successfully.'); + return config; + } catch (error) { + console.error('Failed to load or parse config.toml:', error.message); + process.exit(1); + } +} + +function generateRandomData() { + const randomString = Math.random().toString(36).substring(2, 10); + const username = `user${randomString}`; + const email = `${username}@gmail.com`; + const password = `pass${randomString}`; + const age = Math.floor(Math.random() * 30 + 20).toString(); + const firstName = 'John'; + const lastName = 'Doe'; + const phone = '+31610488717'; + const company = 'Example Corp'; + return { username, email, password, age, firstName, lastName, phone, company }; +} + +function parseLocator(locatorStr) { + locatorStr = locatorStr.trim(); + if (!locatorStr.startsWith('<')) { + return locatorStr; + } + + const getAttr = (attr) => { + const match = locatorStr.match(new RegExp(`${attr}=["']([^"']+)["']`)); + return match ? match[1] : null; + }; + + const tagName = (locatorStr.match(/^<([a-zA-Z0-9]+)/) || [])[1] || ''; + const id = getAttr('id'); + if (id) { + if (/^\d/.test(id)) { + return `${tagName}[id='${id}']`; + } + return `${tagName}#${id}`; + } + + const name = getAttr('name'); + if (name) return `${tagName}[name='${name}']`; + + const placeholder = getAttr('placeholder'); + if (placeholder) return `${tagName}[placeholder='${placeholder}']`; + + const type = getAttr('type'); + if (type) return `${tagName}[type='${type}']`; + + return tagName; +} + +function loadSitesFromFile(filePath) { + const fullPath = path.isAbsolute(filePath) ? filePath : path.join(__dirname, '..', filePath); + const content = fs.readFileSync(fullPath, 'utf-8'); + const urls = content.split('\n').filter(line => line.trim() && !line.startsWith('#')); + return urls.map((url, index) => ({ + name: `Site_${index + 1}`, + url: url.trim(), + category: 'Newsletter', + needsPreSubmitAction: false + })); +} + +module.exports = { + loadConfig, + generateRandomData, + parseLocator, + loadSitesFromFile +}; diff --git a/src/fields.js b/src/fields.js new file mode 100644 index 0000000..fd393b4 --- /dev/null +++ b/src/fields.js @@ -0,0 +1,260 @@ +const FIELD_PATTERNS = { + email: { + priority: 1, + types: ['email'], + names: ['email', 'e-mail', 'mail', 'user_email', 'login_email', 'email_address', 'emailaddress'], + ids: ['email', 'e-mail', 'mail', 'user-email', 'login-email'], + placeholders: ['email', 'e-mail', 'email address', 'your email', 'enter email'], + autocomplete: ['email'] + }, + confirmEmail: { + priority: 2, + types: ['email'], + names: ['confirm_email', 'confirmemail', 'email_confirm', 'verify_email', 'email_verify'], + ids: ['confirm-email', 'confirmemail', 'verify-email', 'email-confirm'], + placeholders: ['confirm email', 'verify email', 're-enter email', 'repeat email'], + mustHaveConfirmIndicator: true + }, + firstName: { + priority: 3, + types: ['text'], + names: ['first_name', 'firstname', 'fname', 'first-name', 'vorname', 'prenom', 'fname'], + ids: ['first-name', 'firstname', 'fname', 'firstName', 'mce-fname'], + placeholders: ['first name', 'firstname', 'first'], + autocomplete: ['given-name', 'first-name'] + }, + lastName: { + priority: 4, + types: ['text'], + names: ['last_name', 'lastname', 'lname', 'surname', 'family_name', 'last-name', 'lname'], + ids: ['last-name', 'lastname', 'lname', 'surname', 'lastName', 'mce-lname'], + placeholders: ['last name', 'lastname', 'surname', 'last'], + autocomplete: ['family-name', 'last-name'] + }, + username: { + priority: 5, + types: ['text'], + names: ['username', 'user_name', 'login', 'usr', 'uname', 'user-id', 'userid', 'account_name', 'login_id'], + ids: ['username', 'user-name', 'login', 'usr', 'uname'], + placeholders: ['username', 'user name', 'login', 'username or email'], + autocomplete: ['username'] + }, + password: { + priority: 6, + types: ['password'], + names: ['password', 'pass', 'pwd', 'user_password', 'login_password'], + ids: ['password', 'pass', 'pwd'], + placeholders: ['password', 'pass', 'enter password'], + autocomplete: ['current-password', 'new-password'] + }, + fullName: { + priority: 7, + types: ['text'], + names: ['full_name', 'fullname', 'name'], + ids: ['full-name', 'fullname', 'name'], + placeholders: ['full name', 'your name', 'complete name'], + autocomplete: ['name'] + }, + phone: { + priority: 8, + types: ['tel', 'text'], + names: ['phone', 'telephone', 'mobile', 'cell', 'cellphone', 'phone_number'], + ids: ['phone', 'telephone', 'mobile', 'cell', 'tel'], + placeholders: ['phone', 'telephone', 'mobile number'], + autocomplete: ['tel'] + }, + age: { + priority: 9, + types: ['number', 'text'], + names: ['age', 'user_age'], + ids: ['age'], + placeholders: ['age', 'your age'] + }, + company: { + priority: 10, + types: ['text'], + names: ['company', 'organization', 'company_name', 'employer'], + ids: ['company', 'organization', 'employer'], + placeholders: ['company', 'organization'], + autocomplete: ['organization'] + } +}; + +async function detectFormFields(page) { + console.log('Auto-detecting form fields...'); + + const detectedFields = await page.evaluate((patterns) => { + const results = {}; + const usedElements = new Set(); + + const inputs = Array.from(document.querySelectorAll('input, select, textarea')).filter(input => { + const type = input.type?.toLowerCase() || 'text'; + const style = window.getComputedStyle(input); + const rect = input.getBoundingClientRect(); + + if (type === 'hidden' || type === 'submit' || type === 'button') return false; + if (input.tabIndex === -1 && input.style.position === 'absolute') return false; + if (rect.width === 0 || rect.height === 0) return false; + if (style.display === 'none' || style.visibility === 'hidden') return false; + + return true; + }); + + const calculateScore = (input, pattern, fieldName) => { + let score = 0; + const inputType = (input.type || 'text').toLowerCase(); + const inputName = (input.name || '').toLowerCase(); + const inputId = (input.id || '').toLowerCase(); + const placeholder = (input.placeholder || '').toLowerCase(); + const autocomplete = (input.autocomplete || '').toLowerCase(); + + if (pattern.types && pattern.types.includes(inputType)) { + score += 5; + } + + if (pattern.names) { + for (const name of pattern.names) { + const nameLower = name.toLowerCase(); + if (inputName === nameLower) { + score += 20; + break; + } else if (inputName.includes(nameLower)) { + if (fieldName === 'firstName' && (inputName.includes('user') || inputName.includes('login'))) { + score -= 5; + } else if (fieldName === 'lastName' && (inputName.includes('user') || inputName.includes('login'))) { + score -= 5; + } else { + score += 10; + } + break; + } + } + } + + if (pattern.ids) { + for (const id of pattern.ids) { + const idLower = id.toLowerCase(); + if (inputId === idLower) { + score += 20; + break; + } else if (inputId.includes(idLower)) { + score += 10; + break; + } + } + } + + if (pattern.placeholders) { + for (const ph of pattern.placeholders) { + const phLower = ph.toLowerCase(); + if (placeholder === phLower) { + score += 15; + break; + } else if (placeholder.includes(phLower)) { + score += 8; + break; + } + } + } + + if (pattern.autocomplete) { + for (const ac of pattern.autocomplete) { + if (autocomplete === ac.toLowerCase()) { + score += 12; + break; + } + } + } + + const labels = input.labels; + if (labels && labels.length > 0) { + const labelText = labels[0].textContent.toLowerCase(); + const fieldNameLower = fieldName.toLowerCase().replace(/([A-Z])/g, ' $1').trim(); + if (labelText.includes(fieldNameLower) || labelText.includes(pattern.names?.[0] || '')) { + score += 15; + } + } + + if (inputName.includes('b_') || inputId.includes('b_')) { + score -= 30; + } + + if (input.disabled || input.readOnly) { + score -= 20; + } + + if (pattern.mustHaveConfirmIndicator) { + const hasConfirm = inputId.includes('confirm') || inputId.includes('verify') || + inputName.includes('confirm') || inputName.includes('verify') || + placeholder.includes('confirm') || placeholder.includes('verify') || + placeholder.includes('re-') || placeholder.includes('again'); + if (!hasConfirm) { + score -= 15; + } + } + + return score; + }; + + const sortedPatterns = Object.entries(patterns).sort((a, b) => a[1].priority - b[1].priority); + + for (const [fieldName, pattern] of sortedPatterns) { + let bestMatch = null; + let bestScore = 0; + let bestIndex = -1; + + for (let i = 0; i < inputs.length; i++) { + if (usedElements.has(i)) continue; + + const input = inputs[i]; + const score = calculateScore(input, pattern, fieldName); + + if (score > bestScore && score >= 10) { + bestScore = score; + bestMatch = input; + bestIndex = i; + } + } + + if (bestMatch && bestIndex >= 0) { + usedElements.add(bestIndex); + + let selector = ''; + if (bestMatch.id) { + selector = `#${bestMatch.id}`; + } else if (bestMatch.name) { + selector = `[name="${bestMatch.name}"]`; + } else if (bestMatch.placeholder) { + selector = `[placeholder="${bestMatch.placeholder}"]`; + } else { + const tagName = bestMatch.tagName.toLowerCase(); + const type = bestMatch.type ? `[type="${bestMatch.type}"]` : ''; + const allSame = Array.from(document.querySelectorAll(`${tagName}${type}`)); + const index = allSame.indexOf(bestMatch) + 1; + selector = `${tagName}${type}:nth-of-type(${index})`; + } + + results[fieldName] = { + selector, + confidence: bestScore, + name: bestMatch.name, + id: bestMatch.id + }; + } + } + + return results; + }, FIELD_PATTERNS); + + const detectedCount = Object.keys(detectedFields).length; + if (detectedCount === 0) { + console.log(' No fields detected'); + } + + return detectedFields; +} + +module.exports = { + FIELD_PATTERNS, + detectFormFields +}; diff --git a/src/processor.js b/src/processor.js new file mode 100644 index 0000000..f83398d --- /dev/null +++ b/src/processor.js @@ -0,0 +1,327 @@ +const { generateRandomData, parseLocator } = require('./config'); +const { detectFormFields } = require('./fields'); +const { detectSubmitButton } = require('./submit'); +const { detectCaptcha, solveSimpleCaptcha, solveHCaptcha } = require('./captcha'); +const { classifyNewsletterForm } = require('./classifier'); +const { detectSubmissionResult } = require('./submission'); + +async function processSite(browser, site, globalCreds, browserConfig, keepOpen = false, dryRun = false) { + let siteFailed = false; + let failureReason = ''; + let page = null; + + try { + let creds; + if (site.credentials && site.credentials.username) { + creds = { ...globalCreds, ...site.credentials }; + } else { + creds = { ...globalCreds }; + } + + if (!creds.username || !creds.email || !creds.password) { + const randomData = generateRandomData(); + creds = { ...randomData, ...creds }; + } + + console.log(`\n${site.url}`); + console.log(`${'-'.repeat(70)}`); + + page = await browser.newPage(); + + if (browserConfig.userAgent) { + await page.setUserAgent(browserConfig.userAgent); + } + + await page.goto(site.url, { waitUntil: 'networkidle2', timeout: 30000 }); + + const captchaInfo = await detectCaptcha(page); + if (captchaInfo.length > 0) { + console.log(`\n CAPTCHA DETECTED:`); + for (const captcha of captchaInfo) { + const visibility = captcha.isVisible ? '(visible)' : '(hidden)'; + console.log(` - ${captcha.name} ${visibility}`); + } + + if (captchaInfo.some(c => c.type === 'mathCaptcha')) { + if (dryRun) { + console.log(` [DRY-RUN] Would solve math CAPTCHA`); + } else { + const solution = await solveSimpleCaptcha(page); + if (solution && solution.answer) { + console.log(` Auto-solving math: ${solution.text} = ${solution.answer}`); + try { + await page.evaluate((answer) => { + const input = document.querySelector('input[name*="captcha"], input[class*="captcha"], #captcha'); + if (input) { + input.value = answer; + input.dispatchEvent(new Event('input', { bubbles: true })); + } + }, solution.answer); + } catch (e) { + console.warn(` Failed to fill math answer: ${e.message}`); + } + } + } + } + + if (captchaInfo.some(c => c.type === 'textCaptcha')) { + console.log(` Text CAPTCHA detected - looking for input field...`); + const textCaptchaFilled = await page.evaluate(() => { + const input = document.querySelector('input[name*="captcha"], input[id*="captcha"], .captcha-input, #captcha, input[placeholder*="captcha" i]'); + if (input) { + return { + found: true, + selector: input.id ? `#${input.id}` : (input.name ? `[name="${input.name}"]` : 'input[name*="captcha"]') + }; + } + return { found: false }; + }); + if (textCaptchaFilled.found) { + console.log(` Text CAPTCHA input found: ${textCaptchaFilled.selector}`); + } + } + + if (captchaInfo.some(c => c.type === 'imageCaptcha' || c.type === 'textCaptcha')) { + if (dryRun) { + console.log(` [DRY-RUN] Would solve image/text CAPTCHA (no API calls)`); + } else { + const solution = await solveSimpleCaptcha(page); + if (solution && solution.answer) { + console.log(` OCR solved CAPTCHA: "${solution.answer}"`); + try { + await page.evaluate((answer) => { + const input = document.querySelector('input[name*="captcha"], input[class*="captcha"], input[id*="captcha"], #captcha, .captcha-input'); + if (input) { + input.value = answer; + input.dispatchEvent(new Event('input', { bubbles: true })); + input.dispatchEvent(new Event('change', { bubbles: true })); + console.log(` Filled CAPTCHA with: ${answer}`); + } + }, solution.answer); + } catch (e) { + console.warn(` Failed to fill CAPTCHA answer: ${e.message}`); + } + } else if (solution && solution.message) { + console.log(` CAPTCHA solving failed: ${solution.message}`); + } + } + } + + const visibleRecaptcha = captchaInfo.filter(c => (c.type === 'recaptcha' || c.type === 'hcaptcha') && c.isVisible); + if (visibleRecaptcha.length > 0) { + siteFailed = true; + failureReason = `CAPTCHA present: ${visibleRecaptcha.map(c => c.name).join(', ')}`; + console.error(` Cannot auto-solve ${visibleRecaptcha.map(c => c.name).join(', ')}`); + return { siteFailed, failureReason, page }; + } else if (captchaInfo.some(c => c.type === 'recaptcha' || c.type === 'hcaptcha')) { + console.log(` CAPTCHA detected but not visible - attempting to proceed anyway`); + } + } + + const formClassification = await classifyNewsletterForm(page); + console.log(`\n Form Classification: ${formClassification.isNewsletter ? 'Newsletter' : 'Not newsletter'} (${formClassification.confidence}/100 confidence)`); + + if (formClassification.signals.positive.length > 0) { + console.log(' Positive signals:'); + formClassification.signals.positive.forEach(signal => console.log(` + ${signal}`)); + } + + if (formClassification.signals.negative.length > 0) { + console.log(' Negative signals:'); + formClassification.signals.negative.forEach(signal => console.log(` - ${signal}`)); + } + + if (!formClassification.isNewsletter && formClassification.confidence < 40) { + siteFailed = true; + failureReason = 'Not a newsletter signup form'; + console.error(` ${failureReason}`); + } + + console.log('\nDETECTED FIELDS:'); + + let fieldsToFill = {}; + let detectedFields = {}; + const definedLocators = site.locators || {}; + + if (site.autoDetect !== false) { + detectedFields = await detectFormFields(page); + for (const [fieldName, fieldInfo] of Object.entries(detectedFields)) { + fieldsToFill[fieldName] = fieldInfo.selector; + } + } + + const hasEmailField = fieldsToFill.email || fieldsToFill.confirmEmail; + if (!hasEmailField) { + siteFailed = true; + failureReason = 'No email field detected'; + console.warn(`\n ${failureReason}`); + } + + const hasSearchInput = await page.evaluate(() => { + const inputs = document.querySelectorAll('input[type="search"], input[name*="search"], input[id*="search"]'); + return inputs.length > 0; + }).catch(() => false); + + if (hasSearchInput && !hasEmailField) { + console.warn(` Search form detected instead of newsletter`); + siteFailed = true; + failureReason = failureReason || 'Search form instead of newsletter'; + } + + for (const [fieldName, locator] of Object.entries(definedLocators)) { + if (fieldName !== 'submit') { + fieldsToFill[fieldName] = parseLocator(locator); + } + } + + if (Object.keys(fieldsToFill).length > 0) { + console.log(`\n ${'FIELD'.padEnd(12)} | ${'SELECTOR'.padEnd(35)} | ${'CONF'.padEnd(4)} | VALUE`); + console.log(' ' + '-'.repeat(75)); + + for (const [fieldName, fieldInfo] of Object.entries(detectedFields)) { + let valueToFill = creds[fieldName]; + + if (fieldName === 'confirmEmail') { + valueToFill = creds['email']; + } + + const selector = fieldInfo.selector.length > 34 ? fieldInfo.selector.substring(0, 31) + '...' : fieldInfo.selector; + const value = valueToFill ? valueToFill.substring(0, 20) : 'N/A'; + + console.log(` ${fieldName.padEnd(12)} | ${selector.padEnd(35)} | ${fieldInfo.confidence.toString().padEnd(4)} | ${value}`); + + if (valueToFill) { + try { + await page.evaluate((sel, val) => { + const el = document.querySelector(sel); + if (el) { + el.value = val; + el.dispatchEvent(new Event('input', { bubbles: true })); + el.dispatchEvent(new Event('change', { bubbles: true })); + } + }, fieldInfo.selector, valueToFill); + } catch (e) { + console.warn(` Failed to fill ${fieldName}: ${e.message}`); + siteFailed = true; + failureReason = `Failed to fill ${fieldName}: ${e.message}`; + } + } + } + console.log(' ' + '-'.repeat(75)); + } + + let submitSelector = null; + if (definedLocators.submit) { + submitSelector = parseLocator(definedLocators.submit); + } else if (site.autoDetect !== false) { + submitSelector = await detectSubmitButton(page); + } + + if (submitSelector) { + try { + const urlBeforeSubmit = page.url(); + await page.click(submitSelector); + console.log(` Submitted (${submitSelector})`); + + await new Promise(r => setTimeout(r, 1500)); + + let submissionResult; + try { + const urlAfterSubmit = page.url(); + if (urlAfterSubmit !== urlBeforeSubmit) { + console.log(` Page navigated to: ${urlAfterSubmit}`); + } + + submissionResult = await detectSubmissionResult(page, urlBeforeSubmit); + } catch (resultError) { + console.log(` Could not detect submission result: ${resultError.message}`); + submissionResult = { failure: false, success: false, signals: ['Could not determine - page may have redirected'] }; + } + + console.log(`\n Submission Result Analysis:`); + + if (submissionResult.signals && submissionResult.signals.length > 0) { + for (const signal of submissionResult.signals) { + console.log(` - ${signal}`); + } + } + + // Check for hCaptcha on redirected pages (like /challenge) + const postSubmitCaptcha = await detectCaptcha(page); + const visibleHCaptcha = postSubmitCaptcha.filter(c => c.type === 'hcaptcha' && c.isVisible); + const hasChallengeRedirect = page.url().includes('/challenge') || page.url().includes('captcha'); + + if ((visibleHCaptcha.length > 0 || hasChallengeRedirect) && !dryRun) { + console.log(` hCaptcha/challenge detected - attempting to solve...`); + const hcaptchaResult = await solveHCaptcha(page, page.url()); + + if (hcaptchaResult && hcaptchaResult.answer) { + console.log(` hCaptcha solved - resubmitting form...`); + + // Wait a moment for the token to be set + await new Promise(r => setTimeout(r, 500)); + + const resubmitSelector = await detectSubmitButton(page); + if (resubmitSelector) { + await page.click(resubmitSelector); + await new Promise(r => setTimeout(r, 3000)); + + const resubmitResult = await detectSubmissionResult(page, urlBeforeSubmit); + if (resubmitResult.success) { + console.log(` SUCCESS after hCaptcha solve!`); + siteFailed = false; + failureReason = ''; + } else if (resubmitResult.failure) { + siteFailed = true; + failureReason = resubmitResult.failureReason || 'Failed after hCaptcha'; + } else { + // Still unknown but hCaptcha was solved - assume success + console.log(` hCaptcha solved, result unknown - assuming success`); + siteFailed = false; + failureReason = ''; + } + } + } else { + console.log(` Could not solve hCaptcha`); + siteFailed = true; + failureReason = 'hCaptcha solving failed'; + } + } else if ((visibleHCaptcha.length > 0 || hasChallengeRedirect) && dryRun) { + console.log(` [DRY-RUN] Would solve hCaptcha/challenge`); + } else if (submissionResult.failure) { + if (siteFailed) { + console.error(` FAILED: ${submissionResult.failureReason}`); + } + } else if (submissionResult.success) { + console.log(` SUCCESS: Subscription appears to have succeeded`); + } else { + console.log(` Result: Unknown (page may have redirected to confirmation)`); + } + + } catch (submitError) { + siteFailed = true; + failureReason = failureReason || `Submit failed: ${submitError.message}`; + console.error(` Submit failed: ${submitError.message}`); + } + } else { + siteFailed = true; + failureReason = failureReason || 'No submit button found'; + console.warn(` ${failureReason}`); + } + + } catch (error) { + siteFailed = true; + failureReason = error.message; + console.error(` Error: ${error.message}`); + } finally { + if (page && !keepOpen) { + await page.close().catch(() => {}); + } + } + + return { siteFailed, failureReason, page }; +} + +module.exports = { + processSite +}; diff --git a/src/submission.js b/src/submission.js new file mode 100644 index 0000000..dd564c2 --- /dev/null +++ b/src/submission.js @@ -0,0 +1,110 @@ +async function detectSubmissionResult(page, urlBeforeSubmit = null) { + const result = await page.evaluate((beforeUrl) => { + const results = { + success: false, + failure: false, + failureReason: null, + currentUrl: window.location.href, + signals: [] + }; + + // Check if URL changed significantly (often indicates success) + if (beforeUrl && window.location.href !== beforeUrl) { + results.signals.push(`URL changed from ${beforeUrl.split('?')[0]} to ${window.location.href.split('?')[0]}`); + } + + const errorSelectors = [ + '.error', '.Error', '.ERROR', + '[class*="error" i]', + '.alert-danger', '.alert-error', + '[class*="failed" i]', + '[class*="invalid" i]', + '[class*="wrong" i]', + '#error', '#Error', + '[role="alert"]', + '.message.error', + '.error-message', + '.form-error', + '[data-form-error]' + ]; + + for (const selector of errorSelectors) { + const el = document.querySelector(selector); + if (el) { + const text = el.textContent?.toLowerCase() || ''; + const style = window.getComputedStyle(el); + if (style.display !== 'none' && + style.visibility !== 'hidden' && + style.opacity !== '0' && + text.length > 0 && + text.length < 500) { + results.failure = true; + results.failureReason = `Error message detected: ${text.substring(0, 100)}`; + results.signals.push(`Error: ${text.substring(0, 100)}`); + break; + } + } + } + + const pageText = document.body.innerText.toLowerCase(); + + const successKeywords = [ + 'thank you', 'thanks for', 'successfully', 'subscribed', 'subscription confirmed', + 'you are now', 'welcome', 'email confirmed', 'verification sent', + 'check your email', 'confirm your email', 'almost done', + 'signed up', 'signed up successfully', 'subscription successful', + 'almost there', 'confirm your subscription', 'verify your email' + ]; + + for (const keyword of successKeywords) { + if (pageText.includes(keyword)) { + results.success = true; + results.signals.push(`Success keyword: "${keyword}"`); + break; + } + } + + const failureKeywords = [ + 'incorrect', 'invalid captcha', 'wrong captcha', 'incorrect captcha', + 'captcha incorrect', 'captcha invalid', 'captcha error', + 'please complete the captcha', 'complete the captcha', + 'try again', 'submission failed', 'email already exists', + 'you have already subscribed', 'already registered' + ]; + + if (!results.success) { + for (const keyword of failureKeywords) { + if (pageText.includes(keyword)) { + results.failure = true; + results.failureReason = `Failure keyword detected: "${keyword}"`; + results.signals.push(`Failure: ${keyword}`); + break; + } + } + } + + // Only check form visibility if we haven't determined success/failure yet + if (!results.success && !results.failure) { + const formStillVisible = document.querySelector('form') !== null; + const emailInputStillVisible = document.querySelector('input[type="email"]') !== null || + document.querySelector('input[name*="email" i]') !== null; + + if (formStillVisible && emailInputStillVisible) { + // Form still visible but no explicit success/failure - could be either + results.signals.push('Form still visible but no explicit result - unknown'); + } else if (!formStillVisible && !emailInputStillVisible) { + // Form disappeared - likely success + results.success = true; + results.signals.push('Form no longer visible - assuming success'); + } + } + + return results; + }, urlBeforeSubmit); + + return result; +} + +module.exports = { + detectSubmissionResult +}; diff --git a/src/submit.js b/src/submit.js new file mode 100644 index 0000000..3f5def6 --- /dev/null +++ b/src/submit.js @@ -0,0 +1,82 @@ +async function detectSubmitButton(page) { + console.log('Auto-detecting submit button...'); + + const submitSelector = await page.evaluate(() => { + const buttons = Array.from(document.querySelectorAll('button[type="submit"], input[type="submit"], button, input[type="button"]')); + let bestMatch = null; + let bestScore = 0; + + buttons.forEach(btn => { + const text = (btn.textContent || btn.value || '').toLowerCase().trim(); + const type = btn.type?.toLowerCase() || ''; + const className = (btn.className || '').toLowerCase(); + let score = 0; + + if (type === 'submit') score += 20; + + if (text.includes('subscribe')) score += 15; + else if (text.includes('sign up')) score += 12; + else if (text.includes('submit')) score += 10; + else if (text.includes('join')) score += 8; + else if (text.includes('register')) score += 8; + else if (text.includes('send')) score += 5; + + if (className.includes('submit')) score += 8; + if (className.includes('subscribe')) score += 8; + + if (className.includes('close') || text.includes('close')) score -= 20; + if (className.includes('cancel') || text.includes('cancel')) score -= 20; + if (className.includes('back') || text.includes('back')) score -= 15; + if (className.includes('drawer')) score -= 15; + + const rect = btn.getBoundingClientRect(); + if (rect.width > 0 && rect.height > 0 && rect.top > 0) { + score += 3; + } + + if (score > bestScore) { + bestScore = score; + bestMatch = btn; + } + }); + + if (bestMatch && bestScore >= 10) { + if (bestMatch.id) return `#${bestMatch.id}`; + if (bestMatch.name) return `[name="${bestMatch.name}"]`; + + const className = bestMatch.className; + if (className) { + const classes = className.split(' ').filter(c => c.length > 0); + for (const cls of classes) { + const clsLower = cls.toLowerCase(); + if (clsLower.includes('submit') || clsLower.includes('subscribe')) { + return `.${cls}`; + } + } + for (const cls of classes) { + if (!cls.toLowerCase().includes('close') && !cls.toLowerCase().includes('cancel')) { + return `.${cls}`; + } + } + } + + const allBtns = Array.from(document.querySelectorAll('button')); + const index = allBtns.indexOf(bestMatch) + 1; + return `button:nth-of-type(${index})`; + } + + return null; + }); + + if (submitSelector) { + console.log(` Submit button found: ${submitSelector}`); + } else { + console.log(' No submit button found'); + } + + return submitSelector; +} + +module.exports = { + detectSubmitButton +}; diff --git a/test_50.txt b/test_50.txt new file mode 100644 index 0000000..f99b713 --- /dev/null +++ b/test_50.txt @@ -0,0 +1,50 @@ +https://immaculatevegan.com/blogs/magazine +https://gate6.vn/ +https://www.sprintzeal.com/ +https://kulalaland.com/ +https://www.goplaces.co.uk/ +https://www.fiftyfiftybottles.com/ +https://jubina.com/blog +https://www.sneakywholefoods.com/ +https://vegnews.com/signup +https://yonayonabeerworks.com/ +https://www.louispowersportsonline.com/ +https://www.hairworthy.co.uk/ +https://endotaspa.com.au/ +https://prestigify.com/ +https://nobones.co/ +https://www.sofamania.com/ +https://dimoradellebalze.com/en/restaurant/ +https://www.42photo.com/ +https://ifinishedmybasement.com/ +https://tettixgames.com/ +https://www.corinnekai.com/raw-honey-raw-self-newsletter/ +https://www.blendnwhip.com/ +https://www.brookebowlin.com/nuance-required +https://www.thatvegandad.net/ +https://comhwa.org.au/blog/newsletters/ +https://hiutdenim.co.uk/ +https://local-lovely.com/ +https://vituswholefoods.com/ +https://theoriginalshabby.it/ +https://rothys.com/en-au +https://www.trustbasket.com/ +https://chelseabrice.com/ +https://wolfsgamingblog.com/ +https://www.leadfuze.com/blog/ +https://thedefiant.io/ +https://www.entrepreneur.com/ +https://pantproject.com/ +https://www.terrebleu.ca/ +https://www.shopify.com/in/blog/shopify-stores +https://www.skooob.com/ +https://mremountain.com/ +https://uncommonjames.com/ +https://www.axios.com/signup/tampa-bay +https://www.dietassassinista.com/blog/tag/African+Cuisine +https://miriamporter.ca/ +https://uppercasemagazine.com/ +https://www.mercadodasrosas.com/ +https://mybageecha.com/ +https://bebemoss.com/ +https://linkwithin.com/