File size: 3,104 Bytes
05b690a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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' });
      
      // Tunggu iframe muncul dan klik jika perlu
      await page.waitForSelector('.cf-turnstile', { timeout: 10000 });
      await sleep(2000);

      // Polling token
      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}`);
});