| |
| |
| |
| |
|
|
| const express = require('express'); |
| const { connect } = require("puppeteer-real-browser"); |
| const fs = require('fs'); |
| const path = require('path'); |
|
|
| const app = express(); |
| const port = process.env.PORT || 7860; |
|
|
| |
| global.browserLimit = 100; |
| global.timeOut = 120000; |
|
|
| const CACHE_DIR = path.join(__dirname, "cache"); |
| const CACHE_TTL = 5 * 60 * 1000; |
| const MAX_CONCURRENT = global.browserLimit; |
|
|
| |
| const taskQueue = []; |
| let activeTasks = 0; |
|
|
| function processQueue() { |
| if (activeTasks >= MAX_CONCURRENT || taskQueue.length === 0) return; |
| const { task, resolve, reject } = taskQueue.shift(); |
| activeTasks++; |
| task() |
| .then(result => { |
| activeTasks--; |
| resolve(result); |
| processQueue(); |
| }) |
| .catch(error => { |
| activeTasks--; |
| reject(error); |
| processQueue(); |
| }); |
| } |
|
|
| function addTask(task) { |
| return new Promise((resolve, reject) => { |
| taskQueue.push({ task, resolve, reject }); |
| processQueue(); |
| }); |
| } |
|
|
| |
| function readCache(type, taskId) { |
| const file = path.join(CACHE_DIR, type, `${taskId}.json`); |
| if (!fs.existsSync(file)) return null; |
| try { |
| const data = JSON.parse(fs.readFileSync(file, 'utf-8')); |
| if (Date.now() - data.timestamp < CACHE_TTL) return data; |
| return null; |
| } catch { return null; } |
| } |
|
|
| function writeCache(type, taskId, value) { |
| const dir = path.join(CACHE_DIR, type); |
| if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); |
| const file = path.join(dir, `${taskId}.json`); |
| const data = { timestamp: Date.now(), ...value }; |
| fs.writeFileSync(file, JSON.stringify(data, null, 2), 'utf-8'); |
| console.log(`cache SAVED: ${type}:${taskId}`); |
| } |
|
|
| |
| setInterval(() => { |
| const types = ["recaptcha2", "recaptcha3", "turnstile", "interstitial"]; |
| const now = Date.now(); |
| types.forEach(type => { |
| const dir = path.join(CACHE_DIR, type); |
| if (!fs.existsSync(dir)) return; |
| fs.readdirSync(dir).forEach(file => { |
| const filePath = path.join(dir, file); |
| try { |
| const data = JSON.parse(fs.readFileSync(filePath, 'utf-8')); |
| if (now - data.timestamp > CACHE_TTL) fs.unlinkSync(filePath); |
| } catch {} |
| }); |
| }); |
| }, 600000); |
|
|
| |
| app.use(express.json()); |
| app.use(express.urlencoded({ extended: true })); |
|
|
| const tasks = {}; |
|
|
| |
| app.get("/health", (req, res) => { |
| res.json({ |
| status: "healthy", |
| activeTasks, |
| queueLength: taskQueue.length, |
| maxConcurrent: MAX_CONCURRENT, |
| memory: process.memoryUsage() |
| }); |
| }); |
|
|
| |
| app.get("/", (req, res) => { |
| res.json({ |
| message: "CAPTCHA Solver API", |
| version: "8.0.0", |
| limits: { concurrent: MAX_CONCURRENT, timeout: global.timeOut }, |
| endpoints: ["/solve", "/health", "/stats"] |
| }); |
| }); |
|
|
| |
| app.post('/solve', async (req, res) => { |
| const { type, domain, siteKey, taskId, action, proxy, isInvisible } = req.body; |
|
|
| |
| if (taskId) { |
| const task = tasks[taskId]; |
| if (!task) return res.status(404).json({ status: "error", message: "Task not found" }); |
| if (task.status === "pending") return res.json({ status: "processing", position: taskQueue.length }); |
| return res.json({ status: task.status, solution: task.solution }); |
| } |
|
|
| |
| const newTaskId = Date.now().toString(36) + Math.random().toString(36).substr(2); |
| tasks[newTaskId] = { status: "pending" }; |
| console.log(`π New task: ${newTaskId}=${type}:${domain}`); |
|
|
| |
| const cached = readCache(type, newTaskId); |
| if (cached) { |
| tasks[newTaskId] = cached; |
| return res.json({ taskId: newTaskId, status: "done", cached: true }); |
| } |
|
|
| try { |
| const result = await addTask(async () => { |
| console.log(`π Processing: ${newTaskId}`); |
| const ctx = await init_browser(proxy?.server); |
| const page = ctx.page; |
|
|
| let solution = {}; |
| switch (type) { |
| case "recaptcha2": |
| solution = await recaptchaV2({ domain, siteKey, action, isInvisible, proxy }, page); |
| break; |
| case "recaptcha3": |
| solution = await recaptchaV3({ domain, siteKey, action, proxy }, page); |
| break; |
| case "turnstile": |
| solution = await turnstile({ domain, siteKey, action, proxy }, page); |
| break; |
| case "interstitial": |
| solution = await interstitial({ domain, proxy }, page); |
| break; |
| default: |
| throw new Error("Invalid type"); |
| } |
|
|
| tasks[newTaskId] = { status: "done", solution }; |
| writeCache(type, newTaskId, tasks[newTaskId]); |
|
|
| await ctx.browser.close(); |
| console.log(`β
Completed: ${newTaskId}`); |
| return solution; |
| }); |
|
|
| res.json({ |
| taskId: newTaskId, |
| status: "queued", |
| position: taskQueue.length, |
| estimatedWait: taskQueue.length * 30000 |
| }); |
| } catch (error) { |
| tasks[newTaskId] = { status: "error", message: error.message }; |
| console.error(`β Failed: ${newTaskId} - ${error.message}`); |
| res.status(500).json({ taskId: newTaskId, status: "error", message: error.message }); |
| } |
| }); |
|
|
| |
| app.get('/solve', (req, res) => { |
| const { taskId } = req.query; |
| if (!taskId) return res.status(400).json({ error: "Missing taskId" }); |
| const task = tasks[taskId]; |
| if (!task) return res.status(404).json({ error: "Task not found" }); |
| if (task.status === "pending") return res.json({ status: "processing", position: taskQueue.length }); |
| return res.json({ status: task.status, solution: task.solution }); |
| }); |
|
|
| |
| async function init_browser(proxyServer = null) { |
| const connectOptions = { |
| headless: false, |
| turnstile: true, |
| connectOption: { |
| defaultViewport: null, |
| args: [ |
| '--no-sandbox', |
| '--disable-setuid-sandbox', |
| '--disable-dev-shm-usage', |
| '--disable-accelerated-2d-canvas', |
| '--disable-gpu', |
| '--window-size=1024,768' |
| ] |
| }, |
| disableXvfb: false, |
| }; |
|
|
| if (proxyServer) connectOptions.connectOption.args.push(`--proxy-server=${proxyServer}`); |
|
|
| const { browser } = await connect(connectOptions); |
| const [page] = await browser.pages(); |
|
|
| |
| await page.setRequestInterception(true); |
| page.on('request', req => { |
| const type = req.resourceType(); |
| if (["image", "stylesheet", "font", "media"].includes(type)) req.abort(); |
| else req.continue(); |
| }); |
|
|
| console.log(`π Browser initialized${proxyServer ? " with proxy" : ""}`); |
| return { browser, page }; |
| } |
|
|
| |
| const recaptchaV2 = require('./Api/recaptcha2'); |
| const recaptchaV3 = require('./Api/recaptcha3'); |
| const turnstile = require('./Api/turnstile'); |
| const interstitial = require('./interstitial'); |
|
|
| |
| app.get('/stats', (req, res) => { |
| res.json({ |
| activeTasks, |
| queueLength: taskQueue.length, |
| maxConcurrent: MAX_CONCURRENT, |
| memory: process.memoryUsage(), |
| uptime: process.uptime() |
| }); |
| }); |
|
|
| |
| app.use((req, res) => res.status(404).json({ message: 'Endpoint not found' })); |
|
|
| |
| app.listen(port, () => { |
| console.log(` |
| ========================================== |
| π CAPTCHA Solver API v8.0.0 |
| π Port: ${port} |
| β‘ Concurrent Limit: ${MAX_CONCURRENT} |
| β±οΈ Timeout: ${global.timeOut}ms |
| π Started: ${new Date().toISOString()} |
| ========================================== |
| `); |
| }); |
|
|