Download solver.js from vipsphi/luadao: direct link, hf CLI and curl.
- Browser
- Download file 6.27 kB
-
https://huggingface.co/spaces/vipsphi/luadao/resolve/main/solver.js
- Command line
-
hf download hf://spaces/vipsphi/luadao/solver.js
-
curl -L -o solver.js https://huggingface.co/spaces/vipsphi/luadao/resolve/main/solver.js
6.27 kB
| const { BrowserWindow, session } = require("electron"); | |
| const logger = require("./logger"); | |
| class RecaptchaSolver { | |
| constructor() { | |
| this.solverWindow = null; | |
| } | |
| findChrome() { | |
| return "Electron"; | |
| } | |
| async createSolverWindow(targetUrl, proxy = null) { | |
| if (this.solverWindow && !this.solverWindow.isDestroyed()) { | |
| this.solverWindow.destroy(); | |
| } | |
| const partition = `temp:solver-${Date.now()}-${Math.random().toString(36).substring(7)}`; | |
| const ses = session.fromPartition(partition); | |
| if (proxy) { | |
| try { | |
| const proxyUrl = new URL(proxy); | |
| const proxyRules = proxyUrl.host; // ip:port | |
| logger.debug(`[RecaptchaSolver] Setting proxy: ${proxyRules}`); | |
| // Store credentials in session for app-level login handler (app.on('login')) | |
| ses.proxyCredentials = { | |
| username: proxyUrl.username, | |
| password: proxyUrl.password | |
| }; | |
| await ses.setProxy({ proxyRules }); | |
| } catch (e) { | |
| logger.error(`[RecaptchaSolver] Proxy format error: ${proxy}. Skipping.`); | |
| } | |
| } | |
| this.solverWindow = new BrowserWindow({ | |
| width: 360, | |
| height: 640, | |
| show: true, | |
| x: -32000, | |
| y: -32000, | |
| frame: false, | |
| skipTaskbar: true, | |
| focusable: false, | |
| webPreferences: { | |
| nodeIntegration: false, | |
| contextIsolation: false, | |
| session: ses, | |
| webSecurity: false, | |
| backgroundThrottling: false, | |
| }, | |
| }); | |
| this.solverWindow.webContents.session.webRequest.onHeadersReceived( | |
| (details, callback) => { | |
| const responseHeaders = Object.assign({}, details.responseHeaders); | |
| if (responseHeaders["content-security-policy"]) | |
| delete responseHeaders["content-security-policy"]; | |
| if (responseHeaders["x-frame-options"]) | |
| delete responseHeaders["x-frame-options"]; | |
| callback({ responseHeaders, cancel: false }); | |
| } | |
| ); | |
| this.solverWindow.webContents.setUserAgent( | |
| "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36" | |
| ); | |
| this.solverWindow.webContents.setAudioMuted(true); | |
| this.solverWindow.webContents.on("did-fail-load", (event, errorCode, errorDescription, validatedURL) => { | |
| logger.warn(`[RecaptchaSolver] Failed to load URL: ${validatedURL} | Error: ${errorDescription} (${errorCode})`); | |
| }); | |
| await this.solverWindow.loadURL(targetUrl); | |
| } | |
| async simulateHumanInteraction() { | |
| if (!this.solverWindow || this.solverWindow.isDestroyed()) return; | |
| const contents = this.solverWindow.webContents; | |
| try { | |
| contents.sendInputEvent({ type: "mouseEnter", x: 10, y: 10 }); | |
| contents.sendInputEvent({ type: "mouseMove", x: 100, y: 100 }); | |
| await new Promise((r) => setTimeout(r, 100)); | |
| contents.sendInputEvent({ type: "mouseMove", x: 200, y: 150 }); | |
| contents.sendInputEvent({ | |
| type: "mouseDown", | |
| x: 200, | |
| y: 150, | |
| button: "left", | |
| clickCount: 1, | |
| }); | |
| await new Promise((r) => setTimeout(r, 50)); | |
| contents.sendInputEvent({ | |
| type: "mouseUp", | |
| x: 200, | |
| y: 150, | |
| button: "left", | |
| clickCount: 1, | |
| }); | |
| } catch (e) { } | |
| } | |
| async getRecaptchaToken(websiteURL, websiteKey, pageAction, proxy = null) { | |
| const FIXED_URL = "https://labs.google"; | |
| const FIXED_KEY = "6LdsFiUsAAAAAIjVDZcuLhaHiDn5nnHVXVRQGeMV"; | |
| try { | |
| await this.createSolverWindow(FIXED_URL, proxy); | |
| await this.simulateHumanInteraction(); | |
| const token = await this.solverWindow.webContents.executeJavaScript( | |
| ` | |
| (async function() { | |
| const siteKey = '${FIXED_KEY}'; | |
| const action = '${pageAction}'; | |
| const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms)); | |
| async function ensureLibrary() { | |
| if (window.grecaptcha && window.grecaptcha.execute) return; | |
| const old = document.getElementById('recaptcha-solver-script'); | |
| if (old) old.remove(); | |
| return new Promise((resolve, reject) => { | |
| const script = document.createElement('script'); | |
| script.id = 'recaptcha-solver-script'; | |
| script.src = 'https://www.google.com/recaptcha/api.js?render=' + siteKey; | |
| script.onload = () => { | |
| // Đợi thêm 1s để library init xong | |
| setTimeout(resolve, 1000); | |
| }; | |
| script.onerror = () => reject("Load script failed"); | |
| document.head.appendChild(script); | |
| }); | |
| } | |
| try { | |
| await ensureLibrary(); | |
| let attempts = 0; | |
| while (!window.grecaptcha || !window.grecaptcha.execute) { | |
| if (attempts++ > 20) throw new Error("Timeout waiting for grecaptcha"); | |
| await wait(200); | |
| } | |
| return new Promise((resolve, reject) => { | |
| window.grecaptcha.ready(() => { | |
| window.grecaptcha.execute(siteKey, { action: action }) | |
| .then(token => resolve(token)) | |
| .catch(err => reject("Execute Error: " + err.message)); | |
| }); | |
| }); | |
| } catch (e) { | |
| return "ERROR: " + e.message; | |
| } | |
| })(); | |
| `, | |
| true | |
| ); | |
| if (!token || typeof token !== "string" || token.startsWith("ERROR:")) { | |
| throw new Error("Token lỗi: " + token); | |
| } | |
| logger.debug(`[Solver] Token generated: ${token.substring(0, 20)}...`); | |
| this.close(); | |
| return token; | |
| } catch (error) { | |
| logger.error(`[Solver] Error: ${error.message}`); | |
| this.close(); | |
| return "ERROR: " + error.message; | |
| } | |
| } | |
| async close() { | |
| if (this.solverWindow && !this.solverWindow.isDestroyed()) { | |
| this.solverWindow.destroy(); | |
| this.solverWindow = null; | |
| } | |
| } | |
| } | |
| module.exports = RecaptchaSolver; | |