Created
July 21, 2021 11:40
-
-
Save landaida/ae1bce58184bdb8dc35b0532a38e4976 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const puppeteer = require("puppeteer-extra"); | |
const pluginStealth = require("puppeteer-extra-plugin-stealth"); | |
const request = require("request-promise-native"); | |
const userAgents = JSON.parse(require('fs').readFileSync(`${__dirname}/src/useragents.json`)); | |
const vm = require('vm'); | |
const { rdn, getMouseMovements } = require("./src/utils"); | |
require("@google-cloud/vision"); | |
// Setup Google Vision Client | |
let client; | |
puppeteer.use(pluginStealth()); | |
async function getHSL(req) { | |
const hsl = await request.get('https://assets.hcaptcha.com/c/58296b80/hsl.js'); | |
return new Promise((resolve, reject) => { | |
const code = ` | |
var self = {}; | |
function atob(a) { | |
return new Buffer.from(a, 'base64').toString('binary'); | |
} | |
${hsl} | |
hsl('${req}').then(resolve).catch(reject) | |
`; | |
vm.runInNewContext(code, { | |
Buffer, | |
resolve, | |
reject, | |
}); | |
}); | |
} | |
async function getAnswers(request_image, tasks) { | |
let answers = new Map(); | |
for (const task of tasks) { | |
await client.objectLocalization(task.datapoint_uri).then((res) => { | |
let [data] = res; | |
if (data.localizedObjectAnnotations.find((i) => i.name.toUpperCase() === request_image.toUpperCase() && i.score > 0.5)) { | |
answers[task.task_key] = "true"; | |
} else { | |
answers[task.task_key] = "false"; | |
} | |
}); | |
} | |
return answers; | |
} | |
async function tryToSolve(userAgent, sitekey, host) { | |
// Create headers | |
let headers = { | |
"Authority": "hcaptcha.com", | |
"Accept": "application/json", | |
"Accept-Language": "en-US,en;q=0.9", | |
"Content-Type": "application/x-www-form-urlencoded", | |
"Origin": "https://assets.hcaptcha.com", | |
"Sec-Fetch-Site": "same-site", | |
"Sec-Fetch-Mode": "cors", | |
"Sec-Fetch-Dest": "empty", | |
"User-Agent": userAgent | |
}; | |
// Check site config | |
let response = await request({ | |
method: 'get', | |
headers, | |
json: true, | |
url: `https://hcaptcha.com/checksiteconfig?host=${host}&sitekey=${sitekey}&sc=1&swa=1` | |
}); | |
let timestamp = Date.now() + rdn(30, 120); | |
// Check for HSJ | |
if (response.c !== undefined && response.c.type !== "hsl") { | |
console.error('Wrong Challenge Type. Retrying.'); | |
return null; | |
} | |
// Setup form for getting tasks list | |
if (response.c === undefined) { | |
form = { | |
sitekey, | |
host, | |
hl: 'en', | |
motionData: { | |
st: timestamp, | |
mm: getMouseMovements(timestamp) | |
}, | |
} | |
} else { | |
form = { | |
sitekey, | |
host, | |
hl: 'en', | |
motionData: { | |
st: timestamp, | |
mm: getMouseMovements(timestamp) | |
}, | |
n: await getHSL(response.c.req), | |
c: JSON.stringify(response.c) | |
} | |
} | |
// Get tasks | |
let getTasks = await request({ | |
method: "post", | |
headers, | |
json: true, | |
url: `https://hcaptcha.com/getcaptcha`, | |
form: form | |
}); | |
if (getTasks.generated_pass_UUID) { | |
return getTasks.generated_pass_UUID; | |
} | |
// Find what the captcha is looking for user's to click | |
if(getTasks && !getTasks.requester_question) { | |
console.error('Wrong tasks list retrying.'); | |
return null; | |
} | |
const requestImageArray = getTasks.requester_question.en.split(" "); | |
let request_image = requestImageArray[requestImageArray.length - 1]; | |
if (request_image === "motorbus") { | |
request_image = "bus" | |
} else { | |
latinLetters='aeioc'.toUpperCase(); | |
otherLetters='аеіοс'.toUpperCase(); | |
request_image = requestImageArray[requestImageArray.length - 1].toUpperCase().split('').map(i=>otherLetters.includes(i) ? latinLetters.split('')[otherLetters.split('').findIndex(item=>i===item)] : i).join(''); | |
} | |
const key = getTasks.key; | |
if (key.charAt(2) === "_") { | |
return key; | |
} | |
const tasks = getTasks.tasklist; | |
const job = getTasks.request_type; | |
timestamp = Date.now() + rdn(30, 120); | |
// Get Answers | |
const answers = await getAnswers(request_image, tasks); | |
// Renew response | |
response = await request({ | |
method: 'get', | |
headers, | |
json: true, | |
url: `https://hcaptcha.com/checksiteconfig?host=${host}&sitekey=${sitekey}&sc=1&swa=1` | |
}); | |
// Setup data for checking answers | |
if (response.c === undefined) { | |
captchaResponse = { | |
job_mode: job, | |
answers, | |
serverdomain: host, | |
sitekey, | |
motionData: JSON.stringify({ | |
st: timestamp, | |
dct: timestamp, | |
mm: getMouseMovements(timestamp), | |
}), | |
n: null, | |
c: "null", | |
} | |
} else { | |
captchaResponse = { | |
job_mode: job, | |
answers, | |
serverdomain: host, | |
sitekey, | |
motionData: JSON.stringify({ | |
st: timestamp, | |
dct: timestamp, | |
mm: getMouseMovements(timestamp), | |
}), | |
n: await getHSL(response.c.req), | |
c: JSON.stringify(response.c) | |
} | |
} | |
// Set new headers | |
headers = { | |
"Authority": "hcaptcha.com", | |
"Accept": "application/json", | |
"Accept-Language": "en-US,en;q=0.9", | |
"Content-Type": "application/json", | |
"Origin": "https://assets.hcaptcha.com", | |
"Sec-Fetch-Site": "same-site", | |
"Sec-Fetch-Mode": "cors", | |
"Sec-Fetch-Dest": "empty", | |
"User-Agent": userAgent, | |
}; | |
// Check answers | |
let checkAnswers; | |
try { | |
checkAnswers = await request(`https://hcaptcha.com/checkcaptcha/${key}`, { | |
method: "post", | |
headers, | |
json: true, | |
body: captchaResponse, | |
}); | |
} catch (error) { | |
console.error('Wrong checkAnswers. Retrying.'); | |
return null; | |
} | |
if (checkAnswers.generated_pass_UUID) { | |
return checkAnswers.generated_pass_UUID; | |
} | |
console.error('Wrong Response. Retrying.'); | |
return null; | |
} | |
async function solveCaptcha(siteKey, host) { | |
try { | |
while (true) { | |
// Get random index for random user agent | |
const randomIndex = Math.round(Math.random() * ((userAgents.length - 1) - 0) + 0) | |
// Attempt to solve hCaptcha | |
const result = await tryToSolve(userAgents[randomIndex].useragent, siteKey, host); | |
if (result && result !== null) { | |
return result; | |
} | |
} | |
} catch (e) { | |
if (e.statusCode === 429) { | |
// Reached rate limit, wait 30 sec | |
console.log('Rate limited. Waiting 30 seconds.'); | |
await new Promise((r) => setTimeout(r, 30000)); | |
} else { | |
throw e; | |
} | |
} | |
} | |
async function hcaptcha(page, visionClient) { | |
// Set client passed in to Google Client | |
client = await visionClient; | |
// Expose the page to our solveCaptcha function so we can utilize it | |
await page.exposeFunction("solveCaptcha", solveCaptcha); | |
// Wait for iframe to load | |
await page.waitForSelector('iframe[src*="assets.hcaptcha.com"]'); | |
const token = await page.evaluate(async () => { | |
// Get hcaptcha iframe so we can get the host value | |
const iframesrc = document.querySelector( | |
'iframe[src*="assets.hcaptcha.com"]' | |
).src; | |
const urlParams = new URLSearchParams(iframesrc); | |
return await solveCaptcha( | |
urlParams.get("sitekey"), | |
urlParams.get("host") | |
); | |
}); | |
await page.evaluate((token) => { | |
document.querySelector('[name="h-captcha-response"]').value = token; | |
document.querySelector('[name="g-recaptcha-response"]').value = token; | |
}, token) | |
return token; | |
} | |
async function hcaptchaToken(url, visionClient) { | |
// Set client passed in to Google Client | |
if (!visionClient) { | |
return undefined | |
} | |
client = await visionClient; | |
const browser = await puppeteer.launch({ | |
ignoreHTTPSErrors: true, | |
headless: true, | |
}); | |
// Get browser pages | |
const [page] = await browser.pages(); | |
await page.goto(url) | |
await page.setDefaultNavigationTimeout(0); | |
// Wait for iframe to load | |
await page.waitForSelector('iframe[src*="assets.hcaptcha.com"]'); | |
let captchaData = await page.evaluate(async () => { | |
// Get hcaptcha iframe so we can get the host value | |
const iframesrc = document.querySelector( | |
'iframe[src*="assets.hcaptcha.com"]' | |
).src; | |
const urlParams = new URLSearchParams(iframesrc); | |
return [urlParams.get('sitekey'), urlParams.get('host')]; | |
}); | |
await browser.close() | |
// Solve Captcha | |
return await solveCaptcha(captchaData[0], captchaData[1]); | |
} | |
module.exports = { hcaptcha, hcaptchaToken }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment