| const express = require('express'); |
| const puppeteer = require('puppeteer-extra'); |
| puppeteer.use(require('puppeteer-extra-plugin-stealth')()); |
| const path = require('path'); |
|
|
| const app = express(); |
| const port = process.env.PORT || 7860; |
|
|
| const HTML_TEMPLATE = `<!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Turnstile Solver</title> |
| <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async></script> |
| <script> |
| function checkToken(){ |
| const el=document.querySelector("[name='cf-turnstile-response']"); |
| if(el && el.value) { |
| window.capturedToken = el.value; |
| } |
| } |
| window.onload=function(){setInterval(checkToken, 500);} |
| </script> |
| </head> |
| <body> |
| <div class="cf-turnstile" id="turnstile-widget"></div> |
| </body> |
| </html>`; |
|
|
| const sleep = (ms) => new Promise(r => setTimeout(r, ms)); |
|
|
| async function solveTurnstile(url, sitekey) { |
| const browser = await puppeteer.launch({ |
| headless: "new", |
| executablePath: process.env.PUPPETEER_EXECUTABLE_PATH || null, |
| args: [ |
| "--no-sandbox", |
| "--disable-setuid-sandbox", |
| "--disable-dev-shm-usage", |
| "--disable-blink-features=AutomationControlled" |
| ] |
| }); |
|
|
| try { |
| const page = await browser.newPage(); |
| const urlFixed = url.endsWith("/") ? url : url + "/"; |
|
|
| await page.setRequestInterception(true); |
| page.on('request', (req) => { |
| if (req.url() === urlFixed && req.resourceType() === 'document') { |
| const html = HTML_TEMPLATE.replace('id="turnstile-widget"', `id="turnstile-widget" data-sitekey="${sitekey}"`); |
| req.respond({ status: 200, contentType: 'text/html', body: html }); |
| } else { |
| req.continue(); |
| } |
| }); |
|
|
| await page.goto(urlFixed, { waitUntil: 'domcontentloaded' }); |
| |
| |
| await page.waitForSelector('.cf-turnstile', { timeout: 10000 }); |
| await sleep(2000); |
|
|
| |
| for (let i = 0; i < 30; i++) { |
| const token = await page.evaluate(() => window.capturedToken || null); |
| if (token) { |
| await browser.close(); |
| return token; |
| } |
| await sleep(1000); |
| } |
| throw new Error("Timeout: Gagal mendapatkan token Turnstile"); |
| } catch (err) { |
| await browser.close(); |
| throw err; |
| } |
| } |
|
|
| app.get('/solve', async (req, res) => { |
| const { url, siteKey } = req.query; |
|
|
| if (!url || !siteKey) { |
| return res.status(400).json({ error: "Parameter 'url' dan 'siteKey' diperlukan" }); |
| } |
|
|
| try { |
| console.log(`Menyelesaikan Turnstile untuk: ${url}`); |
| const token = await solveTurnstile(url, siteKey); |
| res.json({ result: token }); |
| } catch (error) { |
| console.error(error); |
| res.status(500).json({ error: error.message }); |
| } |
| }); |
|
|
| app.get('/', (req, res) => { |
| res.send('Turnstile Solver API sedang berjalan. Gunakan endpoint /solve'); |
| }); |
|
|
| app.listen(port, () => { |
| console.log(`Server berjalan di port ${port}`); |
| }); |