const puppeteer = require('puppeteer-extra'); const StealthPlugin = require('puppeteer-extra-plugin-stealth'); const RecaptchaPlugin = require('puppeteer-extra-plugin-recaptcha'); const FormData = require('form-data'); const fs = require('fs'); const path = require('path'); // Use stealth plugin to avoid detection puppeteer.use(StealthPlugin()); // Add recaptcha plugin (requires 2captcha API key for actual solving, but helps with detection) puppeteer.use( RecaptchaPlugin({ visualFeedback: true, // Show when solving captcha throwOnError: false // Don't throw errors, just try to continue }) ); // Helper function to wait const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms)); // Random delay to appear more human-like const randomWait = (min, max) => wait(Math.floor(Math.random() * (max - min + 1)) + min); // Check if we should wait for manual Cloudflare solving const MANUAL_CLOUDFLARE = process.env.MANUAL_CLOUDFLARE === 'true'; const CLOUDFLARE_WAIT = parseInt(process.env.CLOUDFLARE_WAIT || '30000'); if (MANUAL_CLOUDFLARE) { console.log('⚠️ MANUAL_CLOUDFLARE mode enabled - will wait up to', CLOUDFLARE_WAIT / 1000, 'seconds for manual solving'); } // Function to upload image to uploadnx.zone.id async function uploadToServer(filePath) { return new Promise((resolve, reject) => { const form = new FormData(); form.append('file', fs.createReadStream(filePath)); const options = { method: 'POST', hostname: 'uploadnx.zone.id', path: '/api/upload', headers: form.getHeaders() }; const https = require('https'); const req = https.request(options, (res) => { let data = ''; res.on('data', chunk => { data += chunk; }); res.on('end', () => { if (res.statusCode === 200) { try { const response = JSON.parse(data); resolve(response.short_url); } catch (e) { reject(new Error('Invalid JSON response')); } } else { reject(new Error(`Upload failed with status code: ${res.statusCode}`)); } }); }); req.on('error', reject); form.pipe(req); }); } // Load cookies from JSON file function loadCookies(cookieFilePath) { try { if (fs.existsSync(cookieFilePath)) { const cookieData = JSON.parse(fs.readFileSync(cookieFilePath, 'utf8')); console.log(`✓ Loaded cookies from: ${cookieFilePath}`); console.log(` - ${cookieData.cookies.length} cookies found`); console.log(` - ${Object.keys(cookieData.localStorage || {}).length} localStorage items`); console.log(` - ${Object.keys(cookieData.sessionStorage || {}).length} sessionStorage items`); return cookieData; } else { console.log(`⚠️ Cookie file not found: ${cookieFilePath}`); console.log(' Running without cookies...'); return null; } } catch (error) { console.error(`❌ Error loading cookies: ${error.message}`); return null; } } (async () => { // Look for cookie file in multiple locations const possibleCookieFiles = [ path.join(__dirname, 'cookies.json'), path.join(__dirname, 'betking-cookies.json'), '/mnt/user-data/uploads/cookies.json' ]; let cookieData = null; for (const cookieFile of possibleCookieFiles) { cookieData = loadCookies(cookieFile); if (cookieData) break; } const browser = await puppeteer.launch({ headless: true, args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-blink-features=AutomationControlled', '--disable-web-security', '--disable-features=IsolateOrigins,site-per-process', '--disable-dev-shm-usage', '--disable-accelerated-2d-canvas', '--no-first-run', '--no-zygote', '--disable-gpu', '--disable-notifications', '--disable-popup-blocking', // EXTREME ANTI-DETECTION '--disable-features=IsolateOrigins', '--disable-site-isolation-trials', '--disable-features=BlockInsecurePrivateNetworkRequests', '--disable-blink-features=AutomationControlled', '--excludeSwitches=enable-automation', '--disable-infobars', '--window-size=390,844', '--user-agent=' + (cookieData?.userAgent || 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1'), // Bypass bot detection '--disable-blink-features=AutomationControlled', '--disable-features=VizDisplayCompositor', '--enable-features=NetworkService,NetworkServiceInProcess', '--disable-breakpad', '--disable-component-update', '--disable-domain-reliability', '--disable-sync', '--disable-client-side-phishing-detection', '--disable-features=site-per-process', '--enable-features=NetworkServiceInProcess', // Additional anti-Cloudflare flags '--disable-features=IsolateOrigins,site-per-process,SitePerProcess', '--disable-web-security', '--allow-running-insecure-content', '--disable-features=VizDisplayCompositor', '--disable-ipc-flooding-protection', '--disable-hang-monitor', '--disable-prompt-on-repost', '--disable-background-timer-throttling', '--disable-backgrounding-occluded-windows', '--disable-renderer-backgrounding', '--force-color-profile=srgb', '--disable-features=TranslateUI', '--disable-features=Translate' ], ignoreHTTPSErrors: true, defaultViewport: null, ignoreDefaultArgs: ['--enable-automation', '--enable-blink-features=IdleDetection'] }); const page = await browser.newPage(); // EXTREME: Additional page configurations await page.setBypassCSP(true); // Set extra permissions to look more like real browser const context = browser.defaultBrowserContext(); await context.overridePermissions('https://m.betking.com', [ 'geolocation', 'notifications' ]); try { // Enable JavaScript await page.setJavaScriptEnabled(true); // Use user agent from cookie file if available const userAgent = cookieData?.userAgent || 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Mobile/15E148 Safari/604.1'; await page.setUserAgent(userAgent); console.log(`✓ Using User Agent: ${userAgent}`); // Set realistic mobile viewport to match cookies await page.setViewport({ width: 390, height: 844, isMobile: true, hasTouch: true, deviceScaleFactor: 3 }); // Set additional headers const headers = { 'Accept-Language': 'en-US,en;q=0.9,en-NG;q=0.8', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8', 'Accept-Encoding': 'gzip, deflate, br', 'Connection': 'keep-alive', 'Upgrade-Insecure-Requests': '1', 'Sec-Fetch-Dest': 'document', 'Sec-Fetch-Mode': 'navigate', 'Sec-Fetch-Site': 'none', 'Sec-Fetch-User': '?1', 'Cache-Control': 'max-age=0' }; await page.setExtraHTTPHeaders(headers); // EXTREME: Enable request interception to modify headers on the fly await page.setRequestInterception(true); page.on('request', (request) => { const overrides = { headers: { ...request.headers(), 'sec-ch-ua': '"Not_A Brand";v="8", "Chromium";v="120", "Safari";v="16.6"', 'sec-ch-ua-mobile': '?1', 'sec-ch-ua-platform': '"iOS"', 'DNT': '1', 'Referer': 'https://m.betking.com/', 'Origin': 'https://m.betking.com' } }; request.continue(overrides); }); console.log('✓ Request interception enabled with custom headers'); // CRITICAL: Set cookies BEFORE navigating if (cookieData && cookieData.cookies) { console.log('\n🍪 Pre-loading cookies...'); // First, navigate to the domain to establish context try { // Try login page directly for cookie context await page.goto('https://m.betking.com/en-ng/my-accounts/login', { waitUntil: 'domcontentloaded', timeout: 15000 }); console.log('✓ Login page loaded for cookie context'); // Wait a bit for any Cloudflare JS to run await wait(3000); } catch (baseError) { console.log('⚠️ Initial login page load issue, trying base domain...'); try { await page.goto('https://m.betking.com/', { waitUntil: 'domcontentloaded', timeout: 15000 }); console.log('✓ Base domain loaded'); await wait(3000); } catch (fallbackError) { console.log('⚠️ Base domain also failed, setting domain manually...'); await page.evaluateOnNewDocument(() => { document.domain = 'betking.com'; }); } } // Now set all cookies console.log('Setting cookies...'); let cookiesSet = 0; for (const cookie of cookieData.cookies) { try { await page.setCookie(cookie); cookiesSet++; } catch (error) { // Silently skip problematic cookies } } console.log(`✓ ${cookiesSet}/${cookieData.cookies.length} cookies set successfully`); } // Override WebDriver and other bot detection properties await page.evaluateOnNewDocument(() => { // Remove webdriver property Object.defineProperty(navigator, 'webdriver', { get: () => undefined }); // Override permissions const originalQuery = window.navigator.permissions.query; window.navigator.permissions.query = (parameters) => ( parameters.name === 'notifications' ? Promise.resolve({ state: Notification.permission }) : originalQuery(parameters) ); // Add Chrome object window.chrome = { runtime: {}, loadTimes: function() {}, csi: function() {}, app: {} }; // Override plugins with realistic values Object.defineProperty(navigator, 'plugins', { get: () => [ { 0: {type: "application/x-google-chrome-pdf", suffixes: "pdf", description: "Portable Document Format"}, description: "Portable Document Format", filename: "internal-pdf-viewer", length: 1, name: "Chrome PDF Plugin" } ] }); // Override languages Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] }); // Remove automation indicators delete navigator.__proto__.webdriver; // Mock realistic hardware concurrency Object.defineProperty(navigator, 'hardwareConcurrency', { get: () => 4 }); // Mock realistic device memory Object.defineProperty(navigator, 'deviceMemory', { get: () => 8 }); // Override platform Object.defineProperty(navigator, 'platform', { get: () => 'iPhone' }); // Override vendor Object.defineProperty(navigator, 'vendor', { get: () => 'Apple Computer, Inc.' }); // Mock realistic touch points Object.defineProperty(navigator, 'maxTouchPoints', { get: () => 5 }); // Override toString to avoid detection const originalToString = Function.prototype.toString; Function.prototype.toString = function() { if (this === navigator.permissions.query) { return 'function query() { [native code] }'; } return originalToString.call(this); }; // Remove Headless Chrome detection Object.defineProperty(navigator, 'webdriver', { get: () => false }); // Mock battery navigator.getBattery = () => Promise.resolve({ charging: true, chargingTime: 0, dischargingTime: Infinity, level: 1 }); // Override screen properties Object.defineProperty(screen, 'availHeight', { get: () => 844 }); Object.defineProperty(screen, 'availWidth', { get: () => 390 }); Object.defineProperty(screen, 'height', { get: () => 844 }); Object.defineProperty(screen, 'width', { get: () => 390 }); // Canvas fingerprint spoofing const originalToDataURL = HTMLCanvasElement.prototype.toDataURL; HTMLCanvasElement.prototype.toDataURL = function(type) { const shift = { 'r': Math.floor(Math.random() * 10) - 5, 'g': Math.floor(Math.random() * 10) - 5, 'b': Math.floor(Math.random() * 10) - 5, 'a': Math.floor(Math.random() * 10) - 5 }; const context = this.getContext('2d'); if (context) { const imageData = context.getImageData(0, 0, this.width, this.height); for (let i = 0; i < imageData.data.length; i += 4) { imageData.data[i + 0] = imageData.data[i + 0] + shift.r; imageData.data[i + 1] = imageData.data[i + 1] + shift.g; imageData.data[i + 2] = imageData.data[i + 2] + shift.b; imageData.data[i + 3] = imageData.data[i + 3] + shift.a; } context.putImageData(imageData, 0, 0); } return originalToDataURL.apply(this, arguments); }; // WebGL fingerprint spoofing const getParameter = WebGLRenderingContext.prototype.getParameter; WebGLRenderingContext.prototype.getParameter = function(parameter) { if (parameter === 37445) { return 'Apple GPU'; } if (parameter === 37446) { return 'Apple Inc.'; } return getParameter.call(this, parameter); }; // Audio context fingerprint spoofing const audioContext = window.AudioContext || window.webkitAudioContext; if (audioContext) { const OriginalAudioContext = audioContext; window.AudioContext = function() { const context = new OriginalAudioContext(); const originalCreateOscillator = context.createOscillator; context.createOscillator = function() { const oscillator = originalCreateOscillator.call(context); const originalStart = oscillator.start; oscillator.start = function(when) { return originalStart.call(oscillator, when + Math.random() * 0.0001); }; return oscillator; }; return context; }; } // Remove automation from window delete window.cdc_adoQpoasnfa76pfcZLmcfl_Array; delete window.cdc_adoQpoasnfa76pfcZLmcfl_Promise; delete window.cdc_adoQpoasnfa76pfcZLmcfl_Symbol; // Override Performance API to prevent timing attacks const originalGetEntries = Performance.prototype.getEntries; Performance.prototype.getEntries = function() { const entries = originalGetEntries.call(this); return entries.filter(entry => !entry.name.includes('devtools')); }; // Override Date to add small random variations const originalDate = Date; Date = class extends originalDate { constructor(...args) { if (args.length === 0) { super(); const offset = Math.floor(Math.random() * 10); return new originalDate(this.getTime() + offset); } return new originalDate(...args); } }; Date.prototype = originalDate.prototype; Date.now = function() { return originalDate.now() + Math.floor(Math.random() * 10); }; // Spoof notification permission Object.defineProperty(Notification, 'permission', { get: () => 'default' }); // Mock realistic connection Object.defineProperty(navigator, 'connection', { get: () => ({ effectiveType: '4g', rtt: 50, downlink: 10, saveData: false }) }); // Override outerHeight/Width to match viewport Object.defineProperty(window, 'outerWidth', { get: () => 390 }); Object.defineProperty(window, 'outerHeight', { get: () => 844 }); }); console.log('\n🌐 Navigating to the page...'); await randomWait(500, 1500); // Navigate directly to login page const targetUrl = 'https://m.betking.com/en-ng/my-accounts/login?urlAfterLogin=/en-ng'; console.log(`Target: ${targetUrl}`); let response; try { response = await page.goto(targetUrl, { waitUntil: ['domcontentloaded', 'networkidle2'], timeout: 45000 }); } catch (error) { console.log(`⚠️ Navigation warning: ${error.message}`); console.log('Attempting to continue anyway...'); } if (response) { const statusCode = response.status(); console.log(`Response status: ${statusCode}`); if (statusCode === 403) { console.log('⚠️ Received 403 Forbidden error.'); console.log('Checking for Cloudflare challenge...'); // Check if it's a Cloudflare challenge page const isCloudflare = await page.evaluate(() => { const body = document.body.innerText; const title = document.title; return ( body.includes('Cloudflare') || body.includes('Just a moment') || body.includes('Checking your browser') || title.includes('Just a moment') || document.querySelector('#challenge-form') !== null || document.querySelector('.cf-browser-verification') !== null ); }); if (isCloudflare) { console.log('🔍 Cloudflare challenge detected!'); console.log('⏳ Attempting to solve automatically...'); // Try to click the Cloudflare Turnstile checkbox try { // Wait for checkbox to be clickable await page.waitForSelector('input[type="checkbox"]', { timeout: 5000, visible: true }) .catch(() => console.log('No visible checkbox found, trying iframe...')); // Check for iframe (Cloudflare Turnstile uses iframe) const frames = page.frames(); console.log(`Found ${frames.length} frames, checking for Cloudflare challenge...`); let clicked = false; for (const frame of frames) { try { // Try to find and click checkbox in iframe const checkbox = await frame.$('input[type="checkbox"]'); if (checkbox) { console.log('✓ Found checkbox in iframe, clicking...'); await checkbox.click(); clicked = true; console.log('✓ Checkbox clicked!'); break; } // Try Turnstile specific selector const turnstile = await frame.$('.cf-turnstile'); if (turnstile) { console.log('✓ Found Turnstile widget, clicking...'); await turnstile.click(); clicked = true; console.log('✓ Turnstile clicked!'); break; } // Try clicking the challenge box area const challengeBox = await frame.$('#challenge-stage'); if (challengeBox) { console.log('✓ Found challenge box, clicking...'); await challengeBox.click(); clicked = true; console.log('✓ Challenge box clicked!'); break; } } catch (frameError) { // Continue to next frame } } if (!clicked) { console.log('⚠️ Could not find clickable element, trying main page...'); // Try clicking on main page const mainCheckbox = await page.$('input[type="checkbox"]').catch(() => null); if (mainCheckbox) { await mainCheckbox.click(); console.log('✓ Main page checkbox clicked!'); } } } catch (clickError) { console.log('⚠️ Auto-click failed:', clickError.message); } // Simulate human-like mouse movements try { await wait(500); await page.mouse.move(100, 100); await wait(100); await page.mouse.move(200, 200); await wait(150); await page.mouse.move(150, 300); console.log('✓ Mouse movements simulated'); } catch (mouseError) { console.log('Mouse simulation skipped'); } const waitSeconds = CLOUDFLARE_WAIT / 1000; console.log(`⏳ Waiting for Cloudflare to verify (up to ${waitSeconds} seconds)...`); if (MANUAL_CLOUDFLARE) { console.log('💡 You can manually solve the challenge in the browser window!'); } // Wait for Cloudflare challenge to complete try { await page.waitForFunction( () => { // Check if we've passed the challenge const body = document.body.innerText; const title = document.title; return ( !body.includes('Just a moment') && !body.includes('Checking your browser') && !body.includes('Verify you are human') && !title.includes('Just a moment') && document.querySelector('#challenge-form') === null && document.querySelector('.cf-browser-verification') === null ); }, { timeout: CLOUDFLARE_WAIT, polling: 500 } ); console.log('✓ Cloudflare challenge passed!'); // Take screenshot after passing challenge await wait(2000); await page.screenshot({ path: './after-cloudflare.png', fullPage: true }); console.log('Screenshot after Cloudflare: after-cloudflare.png'); } catch (cfError) { console.log('⚠️ Cloudflare challenge timeout - may need manual intervention'); console.log('Taking screenshot of challenge page...'); await page.screenshot({ path: './cloudflare-challenge.png', fullPage: true }); console.log('Screenshot saved to cloudflare-challenge.png'); // Try to continue anyway console.log('Attempting to continue despite challenge...'); } } else { console.log('Trying alternative approaches...'); // Wait and try reloading await wait(2000); try { await page.reload({ waitUntil: ['domcontentloaded', 'networkidle2'], timeout: 30000 }); console.log('✓ Page reloaded with cookies'); } catch (reloadError) { console.log('Reload had issues, but continuing...'); } } } else if (statusCode >= 400) { console.log(`⚠️ Received error status: ${statusCode}`); } else { console.log('✓ Page loaded successfully'); } } else { console.log('⚠️ No response received, but page may have loaded'); } // Set localStorage and sessionStorage if available if (cookieData) { if (cookieData.localStorage && Object.keys(cookieData.localStorage).length > 0) { console.log('\n💾 Setting localStorage...'); try { await page.evaluate((storage) => { for (const [key, value] of Object.entries(storage)) { try { localStorage.setItem(key, value); } catch (e) { // Skip problematic items } } }, cookieData.localStorage); console.log('✓ localStorage set successfully'); } catch (error) { console.log('⚠️ Could not set localStorage:', error.message); } } if (cookieData.sessionStorage && Object.keys(cookieData.sessionStorage).length > 0) { console.log('\n💾 Setting sessionStorage...'); try { await page.evaluate((storage) => { for (const [key, value] of Object.entries(storage)) { try { sessionStorage.setItem(key, value); } catch (e) { // Skip problematic items } } }, cookieData.sessionStorage); console.log('✓ sessionStorage set successfully'); } catch (error) { console.log('⚠️ Could not set sessionStorage:', error.message); } } // One more reload to ensure everything is applied console.log('\n🔄 Final reload to apply all data...'); try { await page.reload({ waitUntil: ['domcontentloaded'], timeout: 30000 }); console.log('✓ Page reloaded'); } catch (reloadError) { console.log('⚠️ Reload warning:', reloadError.message); console.log('Continuing anyway...'); } } console.log('\n⏳ Waiting 10 seconds...'); await wait(10000); // Take initial screenshot console.log('\n📸 Taking initial screenshot...'); await page.screenshot({ path: './betking-initial.png', fullPage: true }); console.log('\n🔍 Checking if we bypassed 403 - looking for login input...'); // Check for username input field let loginInputFound = false; let bypass403Success = false; try { const usernameInput = await page.$('#username'); if (usernameInput) { loginInputFound = true; bypass403Success = true; console.log('✅ SUCCESS! Found username input - 403 bypass worked!'); console.log('✓ We are on the real login page!'); // Check if input is visible const isVisible = await usernameInput.isIntersectingViewport(); console.log(`Username input is visible: ${isVisible}`); // Get input attributes for confirmation const inputInfo = await page.evaluate(() => { const input = document.querySelector('#username'); if (input) { return { id: input.id, type: input.type, name: input.name, placeholder: input.placeholder, class: input.className }; } return null; }); if (inputInfo) { console.log('📋 Input field details:'); console.log(JSON.stringify(inputInfo, null, 2)); } // Check for other login elements const otherElements = await page.evaluate(() => { return { passwordField: !!document.querySelector('#password') || !!document.querySelector('input[type="password"]'), loginButton: !!document.querySelector('button[type="submit"]'), totalInputs: document.querySelectorAll('input').length, totalButtons: document.querySelectorAll('button').length, pageTitle: document.title, hasLoginForm: !!document.querySelector('form') }; }); console.log('📊 Page structure analysis:'); console.log(` - Password field found: ${otherElements.passwordField}`); console.log(` - Login button found: ${otherElements.loginButton}`); console.log(` - Total inputs: ${otherElements.totalInputs}`); console.log(` - Total buttons: ${otherElements.totalButtons}`); console.log(` - Page title: ${otherElements.pageTitle}`); console.log(` - Has form: ${otherElements.hasLoginForm}`); } else { console.log('❌ Username input NOT found - still blocked or wrong page'); } } catch (error) { console.log(`⚠️ Error checking for input: ${error.message}`); } // Also check if we're still on Cloudflare page const stillOnCloudflare = await page.evaluate(() => { const body = document.body.innerText; const title = document.title; return ( body.includes('Just a moment') || body.includes('Checking your browser') || body.includes('Verify you are human') || title.includes('Just a moment') || body.includes('Cloudflare') ); }); if (stillOnCloudflare) { console.log('⚠️ Still on Cloudflare challenge page'); } else if (!loginInputFound) { console.log('⚠️ Not on Cloudflare, but also not on login page - checking what page we\'re on...'); const pageInfo = await page.evaluate(() => { return { url: window.location.href, title: document.title, bodyText: document.body.innerText.substring(0, 500), h1: document.querySelector('h1')?.innerText, errorMessage: document.querySelector('.error-message')?.innerText || document.querySelector('[class*="error"]')?.innerText }; }); console.log('📄 Current page info:'); console.log(` - URL: ${pageInfo.url}`); console.log(` - Title: ${pageInfo.title}`); console.log(` - H1: ${pageInfo.h1}`); if (pageInfo.errorMessage) { console.log(` - Error message: ${pageInfo.errorMessage}`); } } console.log('\n📸 Taking final screenshot...'); if (bypass403Success) { console.log('🎉 STATUS: 403 BYPASS SUCCESSFUL - Login page reached!'); } else { console.log('❌ STATUS: 403 bypass failed or still on challenge page'); } await page.screenshot({ path: './betking-final.png', fullPage: true }); console.log('✓ Screenshot saved to betking-final.png'); // Upload screenshot to server console.log('\n📤 Uploading screenshot to server...'); try { const uploadUrl = await uploadToServer('./betking-final.png'); console.log('✓ Upload successful!'); console.log('\n🔗 Download link:'); console.log(uploadUrl); console.log('\n📋 Direct link (copy this):'); console.log(uploadUrl); } catch (uploadError) { console.error('❌ Failed to upload to server:', uploadError.message); console.log('Screenshot is still saved locally at: ./betking-final.png'); } } catch (error) { console.error('\n❌ Error occurred:', error.message); console.error('Stack trace:', error.stack); try { // Check if page is still open before trying to screenshot if (!page.isClosed()) { await page.screenshot({ path: './betking-error.png', fullPage: true }); console.log('Error screenshot saved to betking-error.png'); } else { console.log('Page was closed, cannot take error screenshot'); } } catch (screenshotError) { console.error('Could not take error screenshot:', screenshotError.message); } } finally { try { if (browser) { await browser.close(); console.log('\n✅ Browser closed'); } } catch (closeError) { console.log('Browser already closed'); } } })();