const express = require('express'); const cors = require('cors'); const { connect } = require("puppeteer-real-browser"); const app = express(); app.use(cors()); app.use(express.json()); const PORT = process.env.PORT || 7860; const MAX_CONCURRENT_BROWSERS = 3; // Batasi agar RAM HF tidak jebol let activeBrowsers = 0; async function createBrowser(proxy = null) { const options = { headless: false, // Wajib false agar Turnstile mengira ini user asli turnstile: true, // Fitur auto-solve bawaan library (opsional, tapi kita pakai manual logic) args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-accelerated-2d-canvas', '--no-first-run', '--no-zygote', '--disable-gpu', '--window-size=1920,1080', ], // Menggunakan Chrome yang sudah diinstall Dockerfile executablePath: process.env.CHROME_PATH || "/usr/bin/google-chrome-stable", customConfig: {}, connectOption: { defaultViewport: null } }; if (proxy) { options.args.push(`--proxy-server=${proxy.hostname}:${proxy.port}`); } // Connect ke browser const { browser, page } = await connect(options); if (proxy && proxy.username && proxy.password) { await page.authenticate({ username: proxy.username, password: proxy.password }); } return { browser, page }; } // FUNGSI UTAMA: Handle Turnstile dengan cData async function handleTurnstile(page, url, siteKey, cData = null, action = null) { return new Promise(async (resolve, reject) => { // Timeout 60 detik const timeout = setTimeout(() => reject(new Error("Timeout waiting for Turnstile token")), 60000); try { console.log(`[Turnstile] Navigating to Real URL: ${url}`); // 1. Buka URL Asli (PENTING: Jangan pakai konten palsu) await page.goto(url, { waitUntil: "domcontentloaded", timeout: 30000 }); // 2. Inject Logika Render Widget // Kita menyuntikkan script ke browser untuk merender widget sesuai request bot await page.evaluate((key, data, act) => { // Hapus widget yang mungkin sudah ada agar tidak bentrok document.querySelectorAll('.cf-turnstile').forEach(e => e.remove()); document.querySelectorAll('iframe[src*="challenges.cloudflare.com"]').forEach(e => e.remove()); // Buat wadah baru const div = document.createElement('div'); div.id = 'custom-turnstile-container'; div.style.position = 'fixed'; div.style.top = '10px'; div.style.left = '10px'; div.style.zIndex = '99999'; document.body.appendChild(div); // Pastikan library Turnstile ada if (typeof window.turnstile === 'undefined') { const script = document.createElement('script'); script.src = "https://challenges.cloudflare.com/turnstile/v0/api.js"; document.body.appendChild(script); } // Fungsi Callback window.cf_solved_token = null; // Reset token // Tunggu sebentar lalu render setTimeout(() => { if (window.turnstile) { window.turnstile.render('#custom-turnstile-container', { sitekey: key, cData: data, // Masukkan cData di sini action: act, // Masukkan Action di sini callback: function(token) { window.cf_solved_token = token; } }); } }, 1000); }, siteKey, cData, action); // 3. Polling: Cek apakah token sudah muncul di variabel window.cf_solved_token const checkToken = setInterval(async () => { try { const token = await page.evaluate(() => window.cf_solved_token); if (token) { clearInterval(checkToken); clearTimeout(timeout); resolve({ token: token }); } // Cek error box (opsional) // const error = await page.evaluate(() => document.querySelector('...error...')); } catch (e) {} }, 1000); } catch (e) { clearTimeout(timeout); reject(e); } }); } // Endpoint API app.post('/bypass', async (req, res) => { // Tambahkan parameter cdata dan action const { url, mode, siteKey, cdata, action, proxy } = req.body; if (!url || !mode) return res.status(400).json({ status: "error", message: "Missing url or mode" }); if (mode === 'turnstile' && !siteKey) return res.status(400).json({ status: "error", message: "siteKey required for turnstile" }); // Cek Slot Browser if (activeBrowsers >= MAX_CONCURRENT_BROWSERS) { return res.status(429).json({ status: "busy", message: "Server busy, try again later" }); } activeBrowsers++; let browserInstance = null; try { console.log(`[REQ] Processing: ${url} | Mode: ${mode} | cData: ${cdata ? "YES" : "NO"}`); const { browser, page } = await createBrowser(proxy); browserInstance = browser; let result; if (mode === 'turnstile') { // Panggil fungsi handleTurnstile dengan parameter baru result = await handleTurnstile(page, url, siteKey, cdata, action); } else { throw new Error("Invalid mode. Only 'turnstile' supported in this fix."); } console.log(`[SUCCESS] Token: ${result.token.substring(0, 15)}...`); res.json({ status: "success", ...result }); } catch (error) { console.error(`[ERROR] ${error.message}`); res.status(500).json({ status: "error", message: error.message }); } finally { if (browserInstance) { try { await browserInstance.close(); } catch {} } activeBrowsers--; } }); app.get('/', (req, res) => res.send("Turnstile cData Solver Ready")); app.listen(PORT, () => console.log(`Server listening on port ${PORT}`));