real-browser-mcp-server 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/general_issue.yaml +58 -0
- package/.github/SETUP.md +111 -0
- package/.github/workflows/publish.yml +135 -0
- package/Dockerfile +78 -0
- package/LICENSE.md +21 -0
- package/README.md +244 -0
- package/lib/cjs/adblocker.bin +0 -0
- package/lib/cjs/index.js +357 -0
- package/lib/cjs/module/pageController.js +70 -0
- package/lib/cjs/module/turnstile.js +62 -0
- package/lib/esm/adblocker.bin +0 -0
- package/lib/esm/index.mjs +359 -0
- package/lib/esm/module/pageController.mjs +68 -0
- package/lib/esm/module/turnstile.mjs +59 -0
- package/package.json +49 -0
- package/src/ai/action-parser.js +274 -0
- package/src/ai/core.js +378 -0
- package/src/ai/element-finder.js +466 -0
- package/src/ai/index.js +82 -0
- package/src/ai/page-analyzer.js +304 -0
- package/src/ai/selector-healer.js +236 -0
- package/src/index.js +128 -0
- package/src/mcp/handlers.js +5071 -0
- package/src/mcp/index.js +190 -0
- package/src/mcp/server.js +144 -0
- package/src/mcp/tools.js +18 -0
- package/src/shared/tools.js +618 -0
- package/test/cjs/test.js +212 -0
- package/test/esm/package.json +3 -0
- package/test/esm/test.js +176 -0
- package/test-ua.js +47 -0
- package/typings.d.ts +85 -0
package/lib/cjs/index.js
ADDED
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
const { chromium } = require("patchright");
|
|
2
|
+
const { createCursor } = require("ghost-cursor-patchright");
|
|
3
|
+
const { PlaywrightBlocker } = require("@ghostery/adblocker-playwright");
|
|
4
|
+
const { pageController } = require("./module/pageController.js");
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
let adBlockerInstance = null;
|
|
9
|
+
let adBlockerPromise = null;
|
|
10
|
+
function getAdBlocker() {
|
|
11
|
+
if (!adBlockerPromise) {
|
|
12
|
+
const cachePath = path.join(__dirname, 'adblocker.bin');
|
|
13
|
+
adBlockerPromise = PlaywrightBlocker.fromPrebuiltAdsAndTracking(fetch, {
|
|
14
|
+
path: cachePath,
|
|
15
|
+
read: fs.promises.readFile,
|
|
16
|
+
write: fs.promises.writeFile,
|
|
17
|
+
}).then(blocker => {
|
|
18
|
+
adBlockerInstance = blocker;
|
|
19
|
+
return blocker;
|
|
20
|
+
}).catch(err => {
|
|
21
|
+
console.error('[adblocker] Failed to initialize adblocker:', err.message);
|
|
22
|
+
return null;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
return adBlockerPromise;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function loadEnvFile() {
|
|
29
|
+
const envPaths = [
|
|
30
|
+
path.join(process.cwd(), '.env'),
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
let currentDir = process.cwd();
|
|
34
|
+
for (let i = 0; i < 5; i++) {
|
|
35
|
+
const envPath = path.join(currentDir, '.env');
|
|
36
|
+
if (fs.existsSync(envPath) && !envPaths.includes(envPath)) {
|
|
37
|
+
envPaths.push(envPath);
|
|
38
|
+
}
|
|
39
|
+
const parentDir = path.dirname(currentDir);
|
|
40
|
+
if (parentDir === currentDir) break;
|
|
41
|
+
currentDir = parentDir;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
for (const envPath of envPaths) {
|
|
45
|
+
try {
|
|
46
|
+
if (fs.existsSync(envPath)) {
|
|
47
|
+
const envContent = fs.readFileSync(envPath, 'utf-8');
|
|
48
|
+
envContent.split('\n').forEach(line => {
|
|
49
|
+
const trimmed = line.trim();
|
|
50
|
+
if (trimmed && !trimmed.startsWith('#')) {
|
|
51
|
+
const [key, ...valueParts] = trimmed.split('=');
|
|
52
|
+
const value = valueParts.join('=').replace(/^["']|["']$/g, '');
|
|
53
|
+
if (key && !process.env[key]) {
|
|
54
|
+
process.env[key] = value;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
} catch (error) {
|
|
61
|
+
// Silently ignore .env loading errors
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
loadEnvFile();
|
|
67
|
+
|
|
68
|
+
function getDefaultHeadless() {
|
|
69
|
+
const envHeadless = (process.env.HEADLESS || '').toLowerCase();
|
|
70
|
+
return envHeadless === 'true';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function setupRealPage(browser, page) {
|
|
74
|
+
if (page._setupApplied) return page;
|
|
75
|
+
page._setupApplied = true;
|
|
76
|
+
|
|
77
|
+
// Enable ad blocker
|
|
78
|
+
if (adBlockerInstance) {
|
|
79
|
+
adBlockerInstance.enableBlockingInPage(page).catch(() => {});
|
|
80
|
+
} else {
|
|
81
|
+
getAdBlocker().then(blocker => {
|
|
82
|
+
if (blocker) {
|
|
83
|
+
blocker.enableBlockingInPage(page).catch(() => {});
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Ghost Cursor integration - Bézier curve human-like mouse movement
|
|
89
|
+
try {
|
|
90
|
+
const cursor = createCursor(page);
|
|
91
|
+
page.realCursor = {
|
|
92
|
+
move: async (selector, options = {}) => {
|
|
93
|
+
try {
|
|
94
|
+
await cursor.actions.move(selector, options);
|
|
95
|
+
} catch (e) {
|
|
96
|
+
// Fallback to native hover if ghost-cursor fails
|
|
97
|
+
try { await page.hover(selector); } catch (_) {}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
page.realClick = async (selector, options = {}) => {
|
|
102
|
+
try {
|
|
103
|
+
await cursor.actions.click({ target: selector, ...options });
|
|
104
|
+
} catch (e) {
|
|
105
|
+
// Fallback to native click if ghost-cursor fails
|
|
106
|
+
await page.click(selector, options);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
} catch (e) {
|
|
110
|
+
// Fallback if ghost-cursor-patchright fails to initialize
|
|
111
|
+
if (!page.realClick) {
|
|
112
|
+
page.realClick = async (selector, options) => {
|
|
113
|
+
await page.click(selector, options);
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
if (!page.realCursor) {
|
|
117
|
+
page.realCursor = {
|
|
118
|
+
move: async (selector) => {
|
|
119
|
+
try { await page.hover(selector); } catch (_) {}
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return page;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function getBraveExecutablePath() {
|
|
129
|
+
if (process.env.BRAVE_PATH && fs.existsSync(process.env.BRAVE_PATH)) {
|
|
130
|
+
return process.env.BRAVE_PATH;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const platform = process.platform;
|
|
134
|
+
const { execSync } = require('child_process');
|
|
135
|
+
|
|
136
|
+
// Try automatic scanning via CLI / registry query
|
|
137
|
+
if (platform === 'win32') {
|
|
138
|
+
const regQueries = [
|
|
139
|
+
'reg query "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\brave.exe" /ve',
|
|
140
|
+
'reg query "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\brave.exe" /ve',
|
|
141
|
+
'reg query "HKEY_LOCAL_MACHINE\\SOFTWARE\\Clients\\StartMenuInternet\\Brave-Browser\\shell\\open\\command" /ve'
|
|
142
|
+
];
|
|
143
|
+
|
|
144
|
+
for (const cmd of regQueries) {
|
|
145
|
+
try {
|
|
146
|
+
const output = execSync(cmd, { stdio: ['ignore', 'pipe', 'ignore'] }).toString();
|
|
147
|
+
const match = output.match(/REG_SZ\s+(.*)/);
|
|
148
|
+
if (match && match[1]) {
|
|
149
|
+
let p = match[1].trim().replace(/^"|"$/g, '');
|
|
150
|
+
if (!p.toLowerCase().endsWith('.exe')) {
|
|
151
|
+
const exeIndex = p.toLowerCase().indexOf('.exe');
|
|
152
|
+
if (exeIndex !== -1) {
|
|
153
|
+
p = p.substring(0, exeIndex + 4).replace(/^"|"$/g, '');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (fs.existsSync(p)) return p;
|
|
157
|
+
}
|
|
158
|
+
} catch (e) {}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
const output = execSync('where brave.exe', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim().split('\r\n')[0];
|
|
163
|
+
if (output && fs.existsSync(output)) return output;
|
|
164
|
+
} catch (e) {}
|
|
165
|
+
} else if (platform === 'darwin') {
|
|
166
|
+
try {
|
|
167
|
+
const output = execSync('mdfind "kMDItemCFBundleIdentifier == \'com.brave.Browser\'"', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim().split('\n')[0];
|
|
168
|
+
if (output) {
|
|
169
|
+
const p = path.join(output, 'Contents', 'MacOS', 'Brave Browser');
|
|
170
|
+
if (fs.existsSync(p)) return p;
|
|
171
|
+
}
|
|
172
|
+
} catch (e) {}
|
|
173
|
+
} else {
|
|
174
|
+
try {
|
|
175
|
+
const output = execSync('which brave-browser || which brave', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
|
|
176
|
+
if (output && fs.existsSync(output)) return output;
|
|
177
|
+
} catch (e) {}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Fallback to hardcoded common paths
|
|
181
|
+
let paths = [];
|
|
182
|
+
if (platform === 'win32') {
|
|
183
|
+
paths = [
|
|
184
|
+
path.join(process.env.PROGRAMFILES || 'C:\\Program Files', 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
|
|
185
|
+
path.join(process.env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)', 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
|
|
186
|
+
path.join(process.env.LOCALAPPDATA || '', 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe')
|
|
187
|
+
].filter(p => p);
|
|
188
|
+
} else if (platform === 'darwin') {
|
|
189
|
+
paths = [
|
|
190
|
+
'/Applications/Brave Browser.app/Contents/MacOS/Brave Browser'
|
|
191
|
+
];
|
|
192
|
+
} else {
|
|
193
|
+
paths = [
|
|
194
|
+
'/usr/bin/brave-browser',
|
|
195
|
+
'/usr/bin/brave',
|
|
196
|
+
'/usr/bin/brave-browser-stable',
|
|
197
|
+
'/usr/bin/brave-browser-beta',
|
|
198
|
+
'/usr/bin/brave-browser-nightly',
|
|
199
|
+
'/usr/local/bin/brave-browser',
|
|
200
|
+
'/usr/local/bin/brave'
|
|
201
|
+
];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
for (const p of paths) {
|
|
205
|
+
if (p && fs.existsSync(p)) {
|
|
206
|
+
return p;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function applyUserAgentOverride(page, userAgent, userAgentMetadata) {
|
|
214
|
+
try {
|
|
215
|
+
const client = await page.context().newCDPSession(page);
|
|
216
|
+
await client.send('Emulation.setUserAgentOverride', {
|
|
217
|
+
userAgent: userAgent,
|
|
218
|
+
userAgentMetadata: userAgentMetadata
|
|
219
|
+
});
|
|
220
|
+
} catch (e) {
|
|
221
|
+
// Ignore errors
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async function connect({
|
|
226
|
+
args = [],
|
|
227
|
+
headless = getDefaultHeadless(),
|
|
228
|
+
proxy = {},
|
|
229
|
+
turnstile = false,
|
|
230
|
+
executablePath = undefined,
|
|
231
|
+
} = {}) {
|
|
232
|
+
let playwrightProxy = undefined;
|
|
233
|
+
if (proxy && proxy.host && proxy.port) {
|
|
234
|
+
playwrightProxy = {
|
|
235
|
+
server: `${proxy.host}:${proxy.port}`
|
|
236
|
+
};
|
|
237
|
+
if (proxy.username && proxy.password) {
|
|
238
|
+
playwrightProxy.username = proxy.username;
|
|
239
|
+
playwrightProxy.password = proxy.password;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// 1. Launch a temporary browser to retrieve the native user agent and properties
|
|
244
|
+
const tempBrowser = await chromium.launch({
|
|
245
|
+
headless: true,
|
|
246
|
+
args: ['--no-sandbox', '--disable-setuid-sandbox'],
|
|
247
|
+
...(executablePath ? { executablePath } : {}),
|
|
248
|
+
});
|
|
249
|
+
const tempContext = await tempBrowser.newContext();
|
|
250
|
+
const tempPage = await tempContext.newPage();
|
|
251
|
+
let nativeUa = '';
|
|
252
|
+
let isBrave = false;
|
|
253
|
+
try {
|
|
254
|
+
nativeUa = await tempPage.evaluate(() => navigator.userAgent);
|
|
255
|
+
isBrave = await tempPage.evaluate(() => typeof navigator.brave !== 'undefined');
|
|
256
|
+
} catch (e) {
|
|
257
|
+
nativeUa = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/148.0.0.0 Safari/537.36';
|
|
258
|
+
isBrave = executablePath && executablePath.toLowerCase().includes('brave');
|
|
259
|
+
}
|
|
260
|
+
await tempBrowser.close();
|
|
261
|
+
|
|
262
|
+
let modifiedUa = nativeUa.replace(/HeadlessChrome\//g, 'Chrome/');
|
|
263
|
+
const chromeVersionMatch = modifiedUa.match(/Chrome\/([\d.]+)/);
|
|
264
|
+
const chromeVersion = chromeVersionMatch ? chromeVersionMatch[1] : '148.0.0.0';
|
|
265
|
+
const majorVersion = chromeVersion.split('.')[0];
|
|
266
|
+
|
|
267
|
+
const brands = [
|
|
268
|
+
{ brand: 'Chromium', version: majorVersion },
|
|
269
|
+
{ brand: 'Not/A)Brand', version: '99' }
|
|
270
|
+
];
|
|
271
|
+
if (isBrave) {
|
|
272
|
+
brands.unshift({ brand: 'Brave', version: majorVersion });
|
|
273
|
+
} else {
|
|
274
|
+
brands.unshift({ brand: 'Google Chrome', version: majorVersion });
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
let platformName = 'Windows';
|
|
278
|
+
if (nativeUa.includes('Macintosh') || nativeUa.includes('Mac OS X')) {
|
|
279
|
+
platformName = 'macOS';
|
|
280
|
+
} else if (nativeUa.includes('Linux')) {
|
|
281
|
+
platformName = 'Linux';
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const userAgentMetadata = {
|
|
285
|
+
brands: brands,
|
|
286
|
+
mobile: false,
|
|
287
|
+
platform: platformName,
|
|
288
|
+
platformVersion: platformName === 'macOS' ? '14.0.0' : platformName === 'Linux' ? '6.0.0' : '10.0.0',
|
|
289
|
+
architecture: 'x86',
|
|
290
|
+
model: '',
|
|
291
|
+
bitness: '64',
|
|
292
|
+
wow64: false
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const chromiumArgs = [
|
|
296
|
+
`--user-agent=${modifiedUa}`,
|
|
297
|
+
'--disable-blink-features=AutomationControlled',
|
|
298
|
+
'--no-sandbox',
|
|
299
|
+
'--disable-setuid-sandbox',
|
|
300
|
+
...args
|
|
301
|
+
];
|
|
302
|
+
|
|
303
|
+
// If headless is true, we run with headless: false but pass '--headless=new' to args.
|
|
304
|
+
// This triggers Chromium's modern undetected headless mode instead of Playwright's default old headless shell.
|
|
305
|
+
let launchHeadless = headless;
|
|
306
|
+
if (headless === true) {
|
|
307
|
+
launchHeadless = false;
|
|
308
|
+
if (!chromiumArgs.includes('--headless=new')) {
|
|
309
|
+
chromiumArgs.push('--headless=new');
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const browser = await chromium.launch({
|
|
314
|
+
headless: launchHeadless,
|
|
315
|
+
args: chromiumArgs,
|
|
316
|
+
proxy: playwrightProxy,
|
|
317
|
+
...(executablePath ? { executablePath } : {}),
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// Ensure ad blocker is ready
|
|
321
|
+
await getAdBlocker();
|
|
322
|
+
|
|
323
|
+
const context = await browser.newContext({
|
|
324
|
+
viewport: null,
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
let page = await context.newPage();
|
|
328
|
+
|
|
329
|
+
await applyUserAgentOverride(page, modifiedUa, userAgentMetadata);
|
|
330
|
+
|
|
331
|
+
setupRealPage(browser, page);
|
|
332
|
+
|
|
333
|
+
page = await pageController({
|
|
334
|
+
browser,
|
|
335
|
+
page,
|
|
336
|
+
proxy,
|
|
337
|
+
turnstile,
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
context.on('page', async (newPage) => {
|
|
341
|
+
await applyUserAgentOverride(newPage, modifiedUa, userAgentMetadata);
|
|
342
|
+
setupRealPage(browser, newPage);
|
|
343
|
+
await pageController({
|
|
344
|
+
browser,
|
|
345
|
+
page: newPage,
|
|
346
|
+
proxy,
|
|
347
|
+
turnstile,
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
return {
|
|
352
|
+
browser,
|
|
353
|
+
page,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
module.exports = { connect };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
const { checkTurnstile } = require('./turnstile.js');
|
|
2
|
+
|
|
3
|
+
async function pageController({ browser, page, proxy, turnstile }) {
|
|
4
|
+
if (page._pageControllerApplied) return page;
|
|
5
|
+
page._pageControllerApplied = true;
|
|
6
|
+
|
|
7
|
+
let solveStatus = turnstile;
|
|
8
|
+
|
|
9
|
+
page.on('close', () => {
|
|
10
|
+
solveStatus = false;
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
async function turnstileSolver() {
|
|
14
|
+
while (solveStatus) {
|
|
15
|
+
await checkTurnstile({ page }).catch(() => { });
|
|
16
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
17
|
+
}
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (solveStatus) {
|
|
22
|
+
turnstileSolver();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// === POPUP AD BLOCKING ===
|
|
26
|
+
const context = page.context();
|
|
27
|
+
context.on('page', async (newPage) => {
|
|
28
|
+
try {
|
|
29
|
+
const opener = await newPage.opener();
|
|
30
|
+
if (opener) {
|
|
31
|
+
const url = newPage.url();
|
|
32
|
+
const isAdPopup = url === 'about:blank' ||
|
|
33
|
+
url.includes('ad') ||
|
|
34
|
+
url.includes('pop') ||
|
|
35
|
+
url.includes('click') ||
|
|
36
|
+
url.includes('redirect') ||
|
|
37
|
+
url.includes('track');
|
|
38
|
+
if (isAdPopup) {
|
|
39
|
+
await newPage.close().catch(() => { });
|
|
40
|
+
console.error('[popup-blocker] Blocked popup ad:', url.substring(0, 50));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
} catch (e) {
|
|
44
|
+
// Ignore errors
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// NOTE: JS stealth overrides are commented out because Patchright natively handles automation hiding.
|
|
49
|
+
// Manual JS overrides trigger Pixelscan fingerprint masking detectors.
|
|
50
|
+
/*
|
|
51
|
+
await page.addInitScript(() => {
|
|
52
|
+
// ========== HARDWARE CONCURRENCY & DEVICE MEMORY FIX ==========
|
|
53
|
+
Object.defineProperty(navigator, 'hardwareConcurrency', {
|
|
54
|
+
get: () => 8,
|
|
55
|
+
configurable: true
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if ('deviceMemory' in navigator) {
|
|
59
|
+
Object.defineProperty(navigator, 'deviceMemory', {
|
|
60
|
+
get: () => 8,
|
|
61
|
+
configurable: true
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
return page;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = { pageController };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const checkTurnstile = async ({ page }) => {
|
|
2
|
+
try {
|
|
3
|
+
const elements = await page.locator('[name="cf-turnstile-response"]').all();
|
|
4
|
+
if (elements.length <= 0) {
|
|
5
|
+
const coordinates = await page.evaluate(() => {
|
|
6
|
+
let coordinates = [];
|
|
7
|
+
document.querySelectorAll('div').forEach(item => {
|
|
8
|
+
try {
|
|
9
|
+
let itemCoordinates = item.getBoundingClientRect();
|
|
10
|
+
let itemCss = window.getComputedStyle(item);
|
|
11
|
+
if (itemCss.margin == "0px" && itemCss.padding == "0px" && itemCoordinates.width > 290 && itemCoordinates.width <= 310 && !item.querySelector('*')) {
|
|
12
|
+
coordinates.push({ x: itemCoordinates.x, y: item.getBoundingClientRect().y, w: item.getBoundingClientRect().width, h: item.getBoundingClientRect().height });
|
|
13
|
+
}
|
|
14
|
+
} catch (err) { }
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
if (coordinates.length <= 0) {
|
|
18
|
+
document.querySelectorAll('div').forEach(item => {
|
|
19
|
+
try {
|
|
20
|
+
let itemCoordinates = item.getBoundingClientRect();
|
|
21
|
+
if (itemCoordinates.width > 290 && itemCoordinates.width <= 310 && !item.querySelector('*')) {
|
|
22
|
+
coordinates.push({ x: itemCoordinates.x, y: item.getBoundingClientRect().y, w: item.getBoundingClientRect().width, h: item.getBoundingClientRect().height });
|
|
23
|
+
}
|
|
24
|
+
} catch (err) { }
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
return coordinates;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
for (const item of coordinates) {
|
|
31
|
+
try {
|
|
32
|
+
let x = item.x + 30;
|
|
33
|
+
let y = item.y + item.h / 2;
|
|
34
|
+
await page.mouse.click(x, y);
|
|
35
|
+
} catch (err) { }
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
for (const element of elements) {
|
|
41
|
+
try {
|
|
42
|
+
// Get the parent element bounding box
|
|
43
|
+
const box = await element.evaluate(el => {
|
|
44
|
+
const parent = el.parentElement;
|
|
45
|
+
if (!parent) return null;
|
|
46
|
+
const rect = parent.getBoundingClientRect();
|
|
47
|
+
return { x: rect.x, y: rect.y, width: rect.width, height: rect.height };
|
|
48
|
+
});
|
|
49
|
+
if (box) {
|
|
50
|
+
let x = box.x + 30;
|
|
51
|
+
let y = box.y + box.height / 2;
|
|
52
|
+
await page.mouse.click(x, y);
|
|
53
|
+
}
|
|
54
|
+
} catch (err) { }
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
57
|
+
} catch (err) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = { checkTurnstile };
|
|
Binary file
|