| |
| |
| |
| 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; |
| const authToken = process.env.authToken || null; |
|
|
| global.browserLimit = 100; |
| global.timeOut = 300000; |
|
|
| |
| const CACHE_DIR = path.join(__dirname, "cache"); |
| const CACHE_FILE = path.join(CACHE_DIR, "cache.json"); |
| const CACHE_TTL = 5 * 60 * 1000; |
| const CACHE_AUTOSAVE = process.env.CACHE_AUTOSAVE === "true"; |
|
|
| 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')); |
| console.log(`cache check: ${type}:${taskId} => ${data ? "HIT" : "MISS"}`); |
| 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}`); |
| } |
|
|
| function cleanCache() { |
| const types = ["turnstile", "recaptcha3", "recaptcha2", "interstitial", "error"]; |
| const now = Date.now(); |
| const TTL = 60 * 60 * 1000; |
|
|
| 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 > TTL) { |
| fs.unlinkSync(filePath); |
| console.log(`cache expired: ${filePath}`); |
| } |
| } catch { |
| fs.unlinkSync(filePath); |
| } |
| }); |
| }); |
| } |
|
|
| setInterval(cleanCache, 600 * 1000); |
|
|
| app.use(express.json()); |
| app.use(express.urlencoded({ extended: true })); |
| const tasks = {}; |
|
|
| |
| app.get("/", (req, res) => { |
| const baseUrl = `${req.protocol}://${req.get('host')}`; |
| const uptime = process.uptime(); |
| res.json({ |
| message: "Welcome", |
| server: { |
| domain: baseUrl, |
| version: "7.3.0", |
| uptime: `${Math.floor(uptime)} seconds`, |
| limit: global.browserLimit, |
| timeout: global.timeOut, |
| status: "recently running" |
| }, |
| solvers: ["turnstile", "recaptcha2", "recaptcha3", "interstitial"] |
| }); |
| }); |
|
|
|
|
| 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" }); |
| } |
|
|
| return res.json(task); |
| } |
|
|
| const newTaskId = Date.now().toString(36); |
| tasks[newTaskId] = { status: "pending" }; |
| console.log(`New : ${newTaskId}=${type}:${domain}`); |
|
|
| (async () => { |
| try { |
| const ctx = await init_browser(proxy?.server); |
| const page = ctx.page; |
| let result; |
|
|
| switch (type) { |
| case "turnstile": |
| result = await turnstile({ domain, siteKey, action, proxy }, page); |
| tasks[newTaskId] = { status: "done", ...result }; |
| console.log(`done: ${newTaskId}=${type}:${domain}`); |
| if (CACHE_AUTOSAVE) writeCache("turnstile", newTaskId, tasks[newTaskId]); |
| break; |
|
|
| case "interstitial": |
| result = await interstitial({ domain, proxy }, page); |
| tasks[newTaskId] = { status: "done", ...result }; |
| console.log(`done: ${newTaskId}=${type}:${domain}`); |
| if (CACHE_AUTOSAVE) writeCache("interstitial", newTaskId, tasks[newTaskId]); |
| break; |
|
|
| case "recaptcha2": |
| result = await recaptchaV2({ domain, siteKey, action, isInvisible, proxy }, page); |
| tasks[newTaskId] = { status: "done", ...result }; |
| console.log(`done: ${newTaskId}=${type}:${domain}`); |
| if (CACHE_AUTOSAVE) writeCache("recaptcha2", newTaskId, tasks[newTaskId]); |
| break; |
| |
| case "recaptcha3": |
| result = await recaptchaV3({ domain, siteKey, action, proxy }, page); |
| tasks[newTaskId] = { status: "done", ...result }; |
| console.log(`done: ${newTaskId}=${type}:${domain}`); |
| if (CACHE_AUTOSAVE) writeCache("recaptcha3", newTaskId, tasks[newTaskId]); |
| break; |
|
|
| default: |
| tasks[newTaskId] = { status: "error", message: "Invalid type" }; |
| } |
|
|
| await ctx.browser.close(); |
| console.log(`Browser closed ${newTaskId}`); |
| } catch (err) { |
| tasks[newTaskId] = { status: "error", message: "totally failed" }; |
| console.error(`failed: ${newTaskId}=${type}:${domain}`); |
| console.error("Detailed error:", err); |
| } |
| })(); |
|
|
| res.json({ taskId: newTaskId, status: "pending" }); |
| }); |
|
|
| |
| async function init_browser(proxyServer = null) { |
| const connectOptions = { |
| headless: false, |
| turnstile: true, |
| connectOption: { defaultViewport: null }, |
| disableXvfb: false, |
| }; |
| if (proxyServer) connectOptions.args = [`--proxy-server=${proxyServer}`]; |
|
|
| const { browser } = await connect(connectOptions); |
| const [page] = await browser.pages(); |
|
|
| await page.goto('about:blank'); |
| 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(`initialized${proxyServer ? "proxy= " + proxyServer : ""}`); |
|
|
| return { browser, page }; |
| } |
|
|
|
|
| const turnstile = require('./Api/turnstile'); |
| const interstitial = require('./Api/interstitial'); |
| const recaptchaV2 = require('./Api/recaptcha2'); |
| const recaptchaV3 = require('./Api/recaptcha3'); |
|
|
| app.use((req, res) => { |
| res.status(404).json({ message: 'Not Found' }); |
| console.warn(`error: ${req.method} ${req.originalUrl}`); |
| }); |
|
|
| app.listen(port, () => { |
| console.log(`Server running: http://localhost:${port}`); |
| }); |