Commit
·
87be38f
0
Parent(s):
hmm
Browse files- .dockerignore +9 -0
- .gitignore +1 -0
- Dockerfile +28 -0
- LICENSE.md +21 -0
- README.md +11 -0
- package-lock.json +0 -0
- package.json +41 -0
- src/createBrowser.js +35 -0
- src/fakePage.html +30 -0
- src/getSource.js +60 -0
- src/index.js +73 -0
- src/reqValidate.js +58 -0
- src/solveTurnstile.max.js +80 -0
- src/solveTurnstile.min.js +80 -0
- src/wafSession.js +80 -0
.dockerignore
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
node_modules
|
2 |
+
npm-debug.log
|
3 |
+
Dockerfile
|
4 |
+
.dockerignore
|
5 |
+
.git
|
6 |
+
.gitignore
|
7 |
+
README.md
|
8 |
+
.env
|
9 |
+
.env.*
|
.gitignore
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
node_modules
|
Dockerfile
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# unknown
|
2 |
+
FROM node:latest
|
3 |
+
|
4 |
+
RUN apt-get update && apt-get install -y \
|
5 |
+
wget \
|
6 |
+
gnupg \
|
7 |
+
ca-certificates \
|
8 |
+
apt-transport-https \
|
9 |
+
chromium \
|
10 |
+
chromium-driver \
|
11 |
+
xvfb \
|
12 |
+
&& rm -rf /var/lib/apt/lists/*
|
13 |
+
|
14 |
+
ENV CHROME_BIN=/usr/bin/chromium
|
15 |
+
|
16 |
+
WORKDIR /app
|
17 |
+
|
18 |
+
COPY package*.json ./
|
19 |
+
|
20 |
+
RUN npm update
|
21 |
+
RUN npm install
|
22 |
+
# RUN npm i -g pm2
|
23 |
+
COPY . .
|
24 |
+
|
25 |
+
EXPOSE 7860
|
26 |
+
|
27 |
+
#CMD ["pm2-runtime", "src/index.js"]
|
28 |
+
CMD ["node", "src/index.js"]
|
LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT License
|
2 |
+
|
3 |
+
Copyright (c) 2023 - 2024 @zfcsoftware
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
+
of this software and associated documentation files (the "Software"), to deal
|
7 |
+
in the Software without restriction, including without limitation the rights
|
8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
+
copies of the Software, and to permit persons to whom the Software is
|
10 |
+
furnished to do so, subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in all
|
13 |
+
copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21 |
+
SOFTWARE.
|
README.md
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
---
|
2 |
+
title: Cf
|
3 |
+
emoji: 💻
|
4 |
+
colorFrom: gray
|
5 |
+
colorTo: pink
|
6 |
+
sdk: docker
|
7 |
+
pinned: false
|
8 |
+
short_description: idk
|
9 |
+
---
|
10 |
+
|
11 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
package-lock.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
package.json
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "cf-clearance-scraper",
|
3 |
+
"version": "2.1.3",
|
4 |
+
"main": "index.js",
|
5 |
+
"scripts": {
|
6 |
+
"start": "node src/index.js",
|
7 |
+
"test": "node --experimental-vm-modules ./node_modules/.bin/jest --detectOpenHandles --verbose"
|
8 |
+
},
|
9 |
+
"jest": {
|
10 |
+
"testMatch": [
|
11 |
+
"**/tests/**/*.js"
|
12 |
+
],
|
13 |
+
"verbose": true
|
14 |
+
},
|
15 |
+
"keywords": [
|
16 |
+
"cf-clearance",
|
17 |
+
"cloudflare",
|
18 |
+
"waf",
|
19 |
+
"scraper",
|
20 |
+
"puppeteer",
|
21 |
+
"xvfb",
|
22 |
+
"turnstile",
|
23 |
+
"bypass",
|
24 |
+
"undetected",
|
25 |
+
"stealth"
|
26 |
+
],
|
27 |
+
"author": "zfcsoftware",
|
28 |
+
"license": "ISC",
|
29 |
+
"description": "This package is an experimental and educational package created for Cloudflare protections.",
|
30 |
+
"dependencies": {
|
31 |
+
"ajv": "^8.17.1",
|
32 |
+
"ajv-formats": "^3.0.1",
|
33 |
+
"body-parser": "^1.20.3",
|
34 |
+
"cors": "^2.8.5",
|
35 |
+
"dotenv": "^16.4.5",
|
36 |
+
"express": "^4.21.0",
|
37 |
+
"jest": "^29.7.0",
|
38 |
+
"puppeteer-real-browser": "^1.4.0",
|
39 |
+
"supertest": "^7.0.0"
|
40 |
+
}
|
41 |
+
}
|
src/createBrowser.js
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const { connect } = require("puppeteer-real-browser")
|
2 |
+
async function createBrowser() {
|
3 |
+
try {
|
4 |
+
if (global.finished == true) return
|
5 |
+
|
6 |
+
global.browser = null
|
7 |
+
|
8 |
+
// console.log('Launching the browser...');
|
9 |
+
|
10 |
+
const { browser } = await connect({
|
11 |
+
headless: false,
|
12 |
+
turnstile: true,
|
13 |
+
connectOption: { defaultViewport: null },
|
14 |
+
disableXvfb: false,
|
15 |
+
})
|
16 |
+
|
17 |
+
// console.log('Browser launched');
|
18 |
+
|
19 |
+
global.browser = browser;
|
20 |
+
|
21 |
+
browser.on('disconnected', async () => {
|
22 |
+
if (global.finished == true) return
|
23 |
+
console.log('Browser disconnected');
|
24 |
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
25 |
+
await createBrowser();
|
26 |
+
})
|
27 |
+
|
28 |
+
} catch (e) {
|
29 |
+
console.log(e.message);
|
30 |
+
if (global.finished == true) return
|
31 |
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
32 |
+
await createBrowser();
|
33 |
+
}
|
34 |
+
}
|
35 |
+
createBrowser()
|
src/fakePage.html
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
|
4 |
+
<head>
|
5 |
+
<meta charset="UTF-8">
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
7 |
+
<title></title>
|
8 |
+
</head>
|
9 |
+
|
10 |
+
<body>
|
11 |
+
<div class="turnstile"></div>
|
12 |
+
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js?onload=onloadTurnstileCallback" defer></script>
|
13 |
+
<script>
|
14 |
+
window.onloadTurnstileCallback = function () {
|
15 |
+
turnstile.render('.turnstile', {
|
16 |
+
sitekey: '<site-key>',
|
17 |
+
callback: function (token) {
|
18 |
+
var c = document.createElement('input');
|
19 |
+
c.type = 'hidden';
|
20 |
+
c.name = 'cf-response';
|
21 |
+
c.value = token;
|
22 |
+
document.body.appendChild(c);
|
23 |
+
},
|
24 |
+
});
|
25 |
+
};
|
26 |
+
|
27 |
+
</script>
|
28 |
+
</body>
|
29 |
+
|
30 |
+
</html>
|
src/getSource.js
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
function getSource({ url, proxy }) {
|
2 |
+
return new Promise(async (resolve, reject) => {
|
3 |
+
if (!url) return reject("Missing url parameter");
|
4 |
+
const context = await global.browser
|
5 |
+
.createBrowserContext({
|
6 |
+
proxyServer: proxy ? `http://${proxy.host}:${proxy.port}` : undefined, // https://pptr.dev/api/puppeteer.browsercontextoptions
|
7 |
+
})
|
8 |
+
.catch(() => null);
|
9 |
+
if (!context) return reject("Failed to create browser context");
|
10 |
+
|
11 |
+
let isResolved = false;
|
12 |
+
|
13 |
+
var cl = setTimeout(async () => {
|
14 |
+
if (!isResolved) {
|
15 |
+
await context.close();
|
16 |
+
reject("Timeout Error");
|
17 |
+
}
|
18 |
+
}, global.timeOut || 60000);
|
19 |
+
|
20 |
+
try {
|
21 |
+
const page = await context.newPage();
|
22 |
+
|
23 |
+
if (proxy?.username && proxy?.password)
|
24 |
+
await page.authenticate({
|
25 |
+
username: proxy.username,
|
26 |
+
password: proxy.password,
|
27 |
+
});
|
28 |
+
|
29 |
+
await page.setRequestInterception(true);
|
30 |
+
page.on("request", async (request) => request.continue());
|
31 |
+
page.on("response", async (res) => {
|
32 |
+
try {
|
33 |
+
if (
|
34 |
+
[200, 302].includes(res.status()) &&
|
35 |
+
[url, url + "/"].includes(res.url())
|
36 |
+
) {
|
37 |
+
await page
|
38 |
+
.waitForNavigation({ waitUntil: "load", timeout: 5000 })
|
39 |
+
.catch(() => {});
|
40 |
+
const html = await page.content();
|
41 |
+
await context.close();
|
42 |
+
isResolved = true;
|
43 |
+
clearInterval(cl);
|
44 |
+
resolve(html);
|
45 |
+
}
|
46 |
+
} catch (e) {}
|
47 |
+
});
|
48 |
+
await page.goto(url, {
|
49 |
+
waitUntil: "domcontentloaded",
|
50 |
+
});
|
51 |
+
} catch (e) {
|
52 |
+
if (!isResolved) {
|
53 |
+
await context.close();
|
54 |
+
clearInterval(cl);
|
55 |
+
reject(e.message);
|
56 |
+
}
|
57 |
+
}
|
58 |
+
});
|
59 |
+
}
|
60 |
+
module.exports = getSource;
|
src/index.js
ADDED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const express = require('express')
|
2 |
+
const app = express()
|
3 |
+
const port = process.env.PORT || 7860
|
4 |
+
const bodyParser = require('body-parser')
|
5 |
+
const authToken = process.env.authToken || null
|
6 |
+
const cors = require('cors')
|
7 |
+
const reqValidate = require('./reqValidate')
|
8 |
+
|
9 |
+
global.browserLength = 0
|
10 |
+
global.browserLimit = Number(process.env.browserLimit) || 20
|
11 |
+
global.timeOut = Number(process.env.timeOut || 60000)
|
12 |
+
|
13 |
+
app.use(bodyParser.json({}))
|
14 |
+
app.use(bodyParser.urlencoded({ extended: true }))
|
15 |
+
app.use(cors())
|
16 |
+
if (process.env.NODE_ENV !== 'development') {
|
17 |
+
let server = app.listen(port, () => { console.log(`Server running on port ${port}`) })
|
18 |
+
try {
|
19 |
+
server.timeout = global.timeOut
|
20 |
+
} catch (e) { }
|
21 |
+
}
|
22 |
+
if (process.env.SKIP_LAUNCH != 'true') require('./createBrowser')
|
23 |
+
|
24 |
+
const getSource = require('./getSource')
|
25 |
+
const solveTurnstileMin = require('./solveTurnstile.min')
|
26 |
+
const solveTurnstileMax = require('./solveTurnstile.max')
|
27 |
+
const wafSession = require('./wafSession')
|
28 |
+
|
29 |
+
app.get("/", (req, res) => {
|
30 |
+
res.send({ msg: "Hello World" })
|
31 |
+
})
|
32 |
+
|
33 |
+
app.post('/action', async (req, res) => {
|
34 |
+
|
35 |
+
const data = req.body
|
36 |
+
|
37 |
+
const check = reqValidate(data)
|
38 |
+
|
39 |
+
if (check !== true) return res.status(400).json({ code: 400, message: 'Bad Request', schema: check })
|
40 |
+
|
41 |
+
if (authToken && data.authToken !== authToken) return res.status(401).json({ code: 401, message: 'Unauthorized' })
|
42 |
+
|
43 |
+
if (global.browserLength >= global.browserLimit) return res.status(429).json({ code: 429, message: 'Too Many Requests' })
|
44 |
+
|
45 |
+
if (process.env.SKIP_LAUNCH != 'true' && !global.browser) return res.status(500).json({ code: 500, message: 'The scanner is not ready yet. Please try again a little later.' })
|
46 |
+
|
47 |
+
var result = { code: 500 }
|
48 |
+
|
49 |
+
global.browserLength++
|
50 |
+
|
51 |
+
switch (data.mode) {
|
52 |
+
case "source":
|
53 |
+
result = await getSource(data).then(res => { return { source: res, code: 200 } }).catch(err => { return { code: 500, message: err.message } })
|
54 |
+
break;
|
55 |
+
case "turnstile-min":
|
56 |
+
result = await solveTurnstileMin(data).then(res => { return { token: res, code: 200 } }).catch(err => { return { code: 500, message: err.message } })
|
57 |
+
break;
|
58 |
+
case "turnstile-max":
|
59 |
+
result = await solveTurnstileMax(data).then(res => { return { token: res, code: 200 } }).catch(err => { return { code: 500, message: err.message } })
|
60 |
+
break;
|
61 |
+
case "waf-session":
|
62 |
+
result = await wafSession(data).then(res => { return { ...res, code: 200 } }).catch(err => { return { code: 500, message: err.message } })
|
63 |
+
break;
|
64 |
+
}
|
65 |
+
|
66 |
+
global.browserLength--
|
67 |
+
|
68 |
+
res.status(result.code ?? 500).send(result)
|
69 |
+
})
|
70 |
+
|
71 |
+
app.use((req, res) => { res.status(404).json({ code: 404, message: 'Not Found' }) })
|
72 |
+
|
73 |
+
if (process.env.NODE_ENV == 'development') module.exports = app
|
src/reqValidate.js
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const Ajv = require("ajv")
|
2 |
+
const addFormats = require("ajv-formats")
|
3 |
+
|
4 |
+
const ajv = new Ajv()
|
5 |
+
addFormats(ajv)
|
6 |
+
|
7 |
+
const schema = {
|
8 |
+
"type": "object",
|
9 |
+
"properties": {
|
10 |
+
"mode": {
|
11 |
+
"type": "string",
|
12 |
+
"enum": ["source", "turnstile-min", "turnstile-max", "waf-session"],
|
13 |
+
},
|
14 |
+
"proxy": {
|
15 |
+
"type": "object",
|
16 |
+
"properties": {
|
17 |
+
"host": { "type": "string" },
|
18 |
+
"port": { "type": "integer" },
|
19 |
+
"username": { "type": "string" },
|
20 |
+
"password": { "type": "string" }
|
21 |
+
},
|
22 |
+
"additionalProperties": false
|
23 |
+
},
|
24 |
+
"url": {
|
25 |
+
"type": "string",
|
26 |
+
"format": "uri",
|
27 |
+
},
|
28 |
+
"authToken": {
|
29 |
+
"type": "string"
|
30 |
+
},
|
31 |
+
"siteKey": {
|
32 |
+
"type": "string"
|
33 |
+
}
|
34 |
+
},
|
35 |
+
"required": ["mode", "url"],
|
36 |
+
"additionalProperties": false
|
37 |
+
}
|
38 |
+
|
39 |
+
// const data = {
|
40 |
+
// mode: "source",
|
41 |
+
// url: "https://example.com",
|
42 |
+
// proxy: {
|
43 |
+
// host: "localhost",
|
44 |
+
// port: 8080,
|
45 |
+
// username: "test",
|
46 |
+
// password: "test"
|
47 |
+
// },
|
48 |
+
// authToken: "123456"
|
49 |
+
// }
|
50 |
+
|
51 |
+
|
52 |
+
function validate(data) {
|
53 |
+
const valid = ajv.validate(schema, data)
|
54 |
+
if (!valid) return ajv.errors
|
55 |
+
else return true
|
56 |
+
}
|
57 |
+
|
58 |
+
module.exports = validate
|
src/solveTurnstile.max.js
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
const fs = require("fs");
|
2 |
+
function solveTurnstileMin({ url, proxy }) {
|
3 |
+
return new Promise(async (resolve, reject) => {
|
4 |
+
if (!url) return reject("Missing url parameter");
|
5 |
+
|
6 |
+
const context = await global.browser
|
7 |
+
.createBrowserContext({
|
8 |
+
proxyServer: proxy ? `http://${proxy.host}:${proxy.port}` : undefined, // https://pptr.dev/api/puppeteer.browsercontextoptions
|
9 |
+
})
|
10 |
+
.catch(() => null);
|
11 |
+
|
12 |
+
if (!context) return reject("Failed to create browser context");
|
13 |
+
|
14 |
+
let isResolved = false;
|
15 |
+
|
16 |
+
var cl = setTimeout(async () => {
|
17 |
+
if (!isResolved) {
|
18 |
+
await context.close();
|
19 |
+
reject("Timeout Error");
|
20 |
+
}
|
21 |
+
}, global.timeOut || 60000);
|
22 |
+
|
23 |
+
try {
|
24 |
+
const page = await context.newPage();
|
25 |
+
|
26 |
+
if (proxy?.username && proxy?.password)
|
27 |
+
await page.authenticate({
|
28 |
+
username: proxy.username,
|
29 |
+
password: proxy.password,
|
30 |
+
});
|
31 |
+
|
32 |
+
await page.evaluateOnNewDocument(() => {
|
33 |
+
let token = null;
|
34 |
+
async function waitForToken() {
|
35 |
+
while (!token) {
|
36 |
+
try {
|
37 |
+
token = window.turnstile.getResponse();
|
38 |
+
} catch (e) {}
|
39 |
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
40 |
+
}
|
41 |
+
var c = document.createElement("input");
|
42 |
+
c.type = "hidden";
|
43 |
+
c.name = "cf-response";
|
44 |
+
c.value = token;
|
45 |
+
document.body.appendChild(c);
|
46 |
+
}
|
47 |
+
waitForToken();
|
48 |
+
});
|
49 |
+
|
50 |
+
await page.goto(url, {
|
51 |
+
waitUntil: "domcontentloaded",
|
52 |
+
});
|
53 |
+
|
54 |
+
await page.waitForSelector('[name="cf-response"]', {
|
55 |
+
timeout: 60000,
|
56 |
+
});
|
57 |
+
const token = await page.evaluate(() => {
|
58 |
+
try {
|
59 |
+
return document.querySelector('[name="cf-response"]').value;
|
60 |
+
} catch (e) {
|
61 |
+
return null;
|
62 |
+
}
|
63 |
+
});
|
64 |
+
isResolved = true;
|
65 |
+
clearInterval(cl);
|
66 |
+
await context.close();
|
67 |
+
if (!token || token.length < 10) return reject("Failed to get token");
|
68 |
+
return resolve(token);
|
69 |
+
} catch (e) {
|
70 |
+
console.log(e);
|
71 |
+
|
72 |
+
if (!isResolved) {
|
73 |
+
await context.close();
|
74 |
+
clearInterval(cl);
|
75 |
+
reject(e.message);
|
76 |
+
}
|
77 |
+
}
|
78 |
+
});
|
79 |
+
}
|
80 |
+
module.exports = solveTurnstileMin;
|
src/solveTurnstile.min.js
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
function solveTurnstileMin({ url, proxy, siteKey }) {
|
2 |
+
return new Promise(async (resolve, reject) => {
|
3 |
+
if (!url) return reject("Missing url parameter");
|
4 |
+
if (!siteKey) return reject("Missing siteKey parameter");
|
5 |
+
|
6 |
+
const context = await global.browser
|
7 |
+
.createBrowserContext({
|
8 |
+
proxyServer: proxy ? `http://${proxy.host}:${proxy.port}` : undefined, // https://pptr.dev/api/puppeteer.browsercontextoptions
|
9 |
+
})
|
10 |
+
.catch(() => null);
|
11 |
+
if (!context) return reject("Failed to create browser context");
|
12 |
+
|
13 |
+
let isResolved = false;
|
14 |
+
|
15 |
+
var cl = setTimeout(async () => {
|
16 |
+
if (!isResolved) {
|
17 |
+
await context.close();
|
18 |
+
reject("Timeout Error");
|
19 |
+
}
|
20 |
+
}, global.timeOut || 60000);
|
21 |
+
|
22 |
+
try {
|
23 |
+
const page = await context.newPage();
|
24 |
+
|
25 |
+
if (proxy?.username && proxy?.password)
|
26 |
+
await page.authenticate({
|
27 |
+
username: proxy.username,
|
28 |
+
password: proxy.password,
|
29 |
+
});
|
30 |
+
|
31 |
+
await page.setRequestInterception(true);
|
32 |
+
|
33 |
+
page.on("request", async (request) => {
|
34 |
+
if (
|
35 |
+
[url, url + "/"].includes(request.url()) &&
|
36 |
+
request.resourceType() === "document"
|
37 |
+
) {
|
38 |
+
const response = await request.respond({
|
39 |
+
status: 200,
|
40 |
+
contentType: "text/html",
|
41 |
+
body: String(
|
42 |
+
require("fs").readFileSync("./src/fakePage.html")
|
43 |
+
).replace(/<site-key>/g, siteKey),
|
44 |
+
});
|
45 |
+
} else {
|
46 |
+
await request.continue();
|
47 |
+
}
|
48 |
+
});
|
49 |
+
|
50 |
+
await page.goto(url, {
|
51 |
+
waitUntil: "domcontentloaded",
|
52 |
+
});
|
53 |
+
|
54 |
+
await page.waitForSelector('[name="cf-response"]', {
|
55 |
+
timeout: 60000,
|
56 |
+
});
|
57 |
+
|
58 |
+
const token = await page.evaluate(() => {
|
59 |
+
try {
|
60 |
+
return document.querySelector('[name="cf-response"]').value;
|
61 |
+
} catch (e) {
|
62 |
+
return null;
|
63 |
+
}
|
64 |
+
});
|
65 |
+
|
66 |
+
isResolved = true;
|
67 |
+
clearInterval(cl);
|
68 |
+
await context.close();
|
69 |
+
if (!token || token.length < 10) return reject("Failed to get token");
|
70 |
+
return resolve(token);
|
71 |
+
} catch (e) {
|
72 |
+
if (!isResolved) {
|
73 |
+
await context.close();
|
74 |
+
clearInterval(cl);
|
75 |
+
reject(e.message);
|
76 |
+
}
|
77 |
+
}
|
78 |
+
});
|
79 |
+
}
|
80 |
+
module.exports = solveTurnstileMin;
|
src/wafSession.js
ADDED
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
async function findAcceptLanguage(page) {
|
2 |
+
return await page.evaluate(async () => {
|
3 |
+
const result = await fetch("https://httpbin.org/get")
|
4 |
+
.then((res) => res.json())
|
5 |
+
.then(
|
6 |
+
(res) =>
|
7 |
+
res.headers["Accept-Language"] || res.headers["accept-language"]
|
8 |
+
)
|
9 |
+
.catch(() => null);
|
10 |
+
return result;
|
11 |
+
});
|
12 |
+
}
|
13 |
+
|
14 |
+
function getSource({ url, proxy }) {
|
15 |
+
return new Promise(async (resolve, reject) => {
|
16 |
+
if (!url) return reject("Missing url parameter");
|
17 |
+
const context = await global.browser
|
18 |
+
.createBrowserContext({
|
19 |
+
proxyServer: proxy ? `http://${proxy.host}:${proxy.port}` : undefined, // https://pptr.dev/api/puppeteer.browsercontextoptions
|
20 |
+
})
|
21 |
+
.catch(() => null);
|
22 |
+
if (!context) return reject("Failed to create browser context");
|
23 |
+
|
24 |
+
let isResolved = false;
|
25 |
+
|
26 |
+
var cl = setTimeout(async () => {
|
27 |
+
if (!isResolved) {
|
28 |
+
await context.close();
|
29 |
+
reject("Timeout Error");
|
30 |
+
}
|
31 |
+
}, global.timeOut || 60000);
|
32 |
+
|
33 |
+
try {
|
34 |
+
const page = await context.newPage();
|
35 |
+
|
36 |
+
if (proxy?.username && proxy?.password)
|
37 |
+
await page.authenticate({
|
38 |
+
username: proxy.username,
|
39 |
+
password: proxy.password,
|
40 |
+
});
|
41 |
+
let acceptLanguage = await findAcceptLanguage(page);
|
42 |
+
await page.setRequestInterception(true);
|
43 |
+
page.on("request", async (request) => request.continue());
|
44 |
+
page.on("response", async (res) => {
|
45 |
+
try {
|
46 |
+
if (
|
47 |
+
[200, 302].includes(res.status()) &&
|
48 |
+
[url, url + "/"].includes(res.url())
|
49 |
+
) {
|
50 |
+
await page
|
51 |
+
.waitForNavigation({ waitUntil: "load", timeout: 5000 })
|
52 |
+
.catch(() => {});
|
53 |
+
const cookies = await page.cookies();
|
54 |
+
let headers = await res.request().headers();
|
55 |
+
delete headers["content-type"];
|
56 |
+
delete headers["accept-encoding"];
|
57 |
+
delete headers["accept"];
|
58 |
+
delete headers["content-length"];
|
59 |
+
headers["accept-language"] = acceptLanguage;
|
60 |
+
await context.close();
|
61 |
+
isResolved = true;
|
62 |
+
clearInterval(cl);
|
63 |
+
resolve({ cookies, headers });
|
64 |
+
}
|
65 |
+
} catch (e) {}
|
66 |
+
});
|
67 |
+
|
68 |
+
await page.goto(url, {
|
69 |
+
waitUntil: "domcontentloaded",
|
70 |
+
});
|
71 |
+
} catch (e) {
|
72 |
+
if (!isResolved) {
|
73 |
+
await context.close();
|
74 |
+
clearInterval(cl);
|
75 |
+
reject(e.message);
|
76 |
+
}
|
77 |
+
}
|
78 |
+
});
|
79 |
+
}
|
80 |
+
module.exports = getSource;
|