XBotz commited on
Commit
3c0e9c6
·
unverified ·
1 Parent(s): ca73035

Update cloudflare-solver.js

Browse files
Files changed (1) hide show
  1. cloudflare-solver.js +293 -0
cloudflare-solver.js CHANGED
@@ -1 +1,294 @@
 
1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use strict';
2
 
3
+ const {
4
+ connect
5
+ } = require('puppeteer-real-browser');
6
+ const fs = require('fs');
7
+
8
+ const WIDTH = 1280;
9
+ const HEIGHT = 800;
10
+
11
+ const ChallengePlatform = {
12
+ JAVASCRIPT: 'non-interactive',
13
+ MANAGED: 'managed',
14
+ INTERACTIVE: 'interactive',
15
+ };
16
+
17
+ const FALLBACK_UA =
18
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ' +
19
+ '(KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36';
20
+
21
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
22
+
23
+ function loadLines(fp) {
24
+ try {
25
+ return fs.readFileSync(fp, 'utf8').split('\n').map(l => l.trim()).filter(Boolean);
26
+ } catch {
27
+ return [];
28
+ }
29
+ }
30
+
31
+ function randomChoice(arr) {
32
+ return arr[Math.floor(Math.random() * arr.length)];
33
+ }
34
+
35
+ function getChromeUA() {
36
+ const agents = loadLines('data/useragents.txt');
37
+ return agents.length ? randomChoice(agents) : FALLBACK_UA;
38
+ }
39
+
40
+ function parseProxy(url) {
41
+ const u = new URL(url);
42
+ return {
43
+ serverUrl: `${u.protocol}//${u.hostname}${u.port ? ':' + u.port : ''}`,
44
+ username: u.username || null,
45
+ password: u.password || null,
46
+ };
47
+ }
48
+
49
+ function extractClearance(cookies) {
50
+ return cookies.find(c => c.name === 'cf_clearance') || null;
51
+ }
52
+
53
+ function buildCookieString(cookies) {
54
+ return cookies.map(c => `${c.name}=${c.value}`).join('; ');
55
+ }
56
+
57
+ async function detectChallenge(page) {
58
+ try {
59
+ const html = await page.content();
60
+ for (const val of Object.values(ChallengePlatform)) {
61
+ if (html.includes(`cType: '${val}'`)) return val;
62
+ }
63
+ return null;
64
+ } catch {
65
+ return null;
66
+ }
67
+ }
68
+
69
+ async function findCFFrame(page) {
70
+ try {
71
+ const frame = page.frames().find(f => {
72
+ try {
73
+ return f.url().includes('challenges.cloudflare.com');
74
+ } catch {
75
+ return false;
76
+ }
77
+ });
78
+ if (frame) return frame;
79
+ } catch {
80
+ /* ignore */ }
81
+
82
+ try {
83
+ const el = await page.$('iframe[src*="challenges.cloudflare.com"]');
84
+ if (el) return await el.contentFrame();
85
+ } catch {
86
+ /* ignore */ }
87
+
88
+ return null;
89
+ }
90
+
91
+ async function clickCFFrame(page, frame) {
92
+ await sleep(1500);
93
+
94
+ try {
95
+ const cb = await frame.$('input[type="checkbox"]').catch(() => null);
96
+ if (cb) {
97
+ const box = await cb.boundingBox().catch(() => null);
98
+ if (box) {
99
+ await cb.click();
100
+ return 'checkbox';
101
+ }
102
+ }
103
+
104
+ const label = await frame.$('label').catch(() => null);
105
+ if (label) {
106
+ const box = await label.boundingBox().catch(() => null);
107
+ if (box) {
108
+ await label.click();
109
+ return 'label';
110
+ }
111
+ }
112
+
113
+ const widget = await frame.$('.cf-turnstile-content, #cf-stage, .ctp-checkbox-label').catch(() => null);
114
+ if (widget) {
115
+ const box = await widget.boundingBox().catch(() => null);
116
+ if (box) {
117
+ await widget.click();
118
+ return 'widget';
119
+ }
120
+ }
121
+
122
+ const body = await frame.$('body').catch(() => null);
123
+ if (body) {
124
+ const box = await body.boundingBox().catch(() => null);
125
+ if (box && box.width > 0) {
126
+ await body.click();
127
+ return 'body';
128
+ }
129
+ }
130
+ } catch (e) {}
131
+
132
+ return null;
133
+ }
134
+
135
+ async function solveChallenge(page, timeoutSecs = 30) {
136
+ const deadline = Date.now() + timeoutSecs * 1000;
137
+ let attempt = 0;
138
+ let lastClicked = 0;
139
+
140
+ while (Date.now() < deadline) {
141
+ attempt++;
142
+
143
+ const cookies = await page.cookies().catch(() => []);
144
+ if (extractClearance(cookies)) {
145
+ return;
146
+ }
147
+
148
+ const platform = await detectChallenge(page);
149
+ if (!platform) {
150
+ return;
151
+ }
152
+
153
+ const frame = await findCFFrame(page);
154
+ if (!frame) {
155
+ await sleep(500);
156
+ continue;
157
+ }
158
+
159
+ if (Date.now() - lastClicked < 5000) {
160
+ await sleep(500);
161
+ continue;
162
+ }
163
+
164
+ const result = await clickCFFrame(page, frame);
165
+ if (result) {
166
+ lastClicked = Date.now();
167
+ await sleep(2500);
168
+ } else {
169
+ await sleep(500);
170
+ }
171
+ }
172
+ }
173
+
174
+ async function solveCloudflare({
175
+ url,
176
+ headless = true,
177
+ proxy = null,
178
+ proxyFile = null,
179
+ timeout = 30,
180
+ } = {}) {
181
+ const proxies = proxyFile ? loadLines(proxyFile) : [];
182
+ const chosenProxy = proxy || (proxies.length ? randomChoice(proxies) : null);
183
+ const userAgent = getChromeUA();
184
+ const parsed = chosenProxy ? parseProxy(chosenProxy) : null;
185
+
186
+ const args = [
187
+ '--no-sandbox',
188
+ '--disable-setuid-sandbox',
189
+ '--disable-quic',
190
+ `--window-size=${WIDTH},${HEIGHT}`,
191
+ ];
192
+ if (parsed) args.push(`--proxy-server=${parsed.serverUrl}`);
193
+
194
+ const {
195
+ browser,
196
+ page
197
+ } = await connect({
198
+ headless,
199
+ args,
200
+ turnstile: false,
201
+ connectOption: {},
202
+ disableXvfb: false,
203
+ ignoreAllFlags: false,
204
+ });
205
+
206
+ try {
207
+ await page.setViewport({
208
+ width: WIDTH,
209
+ height: HEIGHT
210
+ });
211
+ await page.setUserAgent(userAgent);
212
+
213
+ if (parsed?.username) {
214
+ await page.authenticate({
215
+ username: parsed.username,
216
+ password: parsed.password
217
+ });
218
+ }
219
+
220
+ try {
221
+ await page.goto(url, {
222
+ waitUntil: 'domcontentloaded',
223
+ timeout: timeout * 1000
224
+ });
225
+ } catch (e) {
226
+ await browser.close();
227
+ return {
228
+ url,
229
+ success: false,
230
+ error: `Navigation error: ${e.message}`
231
+ };
232
+ }
233
+
234
+ await sleep(2000);
235
+
236
+ let cookies = await page.cookies().catch(() => []);
237
+ let clearance = extractClearance(cookies);
238
+
239
+ if (!clearance) {
240
+ const platform = await detectChallenge(page);
241
+
242
+ if (!platform) {
243
+ await browser.close();
244
+ return {
245
+ url,
246
+ success: false,
247
+ error: 'No Cloudflare challenge detected'
248
+ };
249
+ }
250
+
251
+ await solveChallenge(page, timeout);
252
+
253
+ cookies = await page.cookies().catch(() => []);
254
+ clearance = extractClearance(cookies);
255
+ }
256
+
257
+ const finalUA = await page.evaluate(() => navigator.userAgent).catch(() => userAgent);
258
+
259
+ await browser.close();
260
+
261
+ if (!clearance) {
262
+ return {
263
+ url,
264
+ success: false,
265
+ error: 'Failed to obtain cf_clearance cookie'
266
+ };
267
+ }
268
+
269
+ const cookieString = buildCookieString(cookies);
270
+ const unixTimestamp = Math.floor(clearance.expires - 365 * 24 * 3600);
271
+ const timestamp = new Date(unixTimestamp * 1000).toISOString();
272
+
273
+ return {
274
+ success: true,
275
+ url,
276
+ proxy: chosenProxy,
277
+ user_agent: finalUA,
278
+ cf_clearance: clearance.value,
279
+ all_cookies: cookies,
280
+ cookie_string: cookieString,
281
+ unix_timestamp: unixTimestamp,
282
+ timestamp,
283
+ domain: clearance.domain,
284
+ };
285
+
286
+ } catch (err) {
287
+ await browser.close().catch(() => {});
288
+ throw err;
289
+ }
290
+ }
291
+
292
+ module.exports = {
293
+ solveCloudflare
294
+ };