real-browser-mcp-server 1.1.6 → 1.1.8

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.
Files changed (47) hide show
  1. package/README.md +4 -8
  2. package/dist/lib/cjs/index.js +384 -0
  3. package/{lib → dist/lib}/cjs/module/pageController.js +27 -29
  4. package/{lib → dist/lib}/cjs/module/turnstile.js +23 -12
  5. package/dist/src/ai/action-parser.js +229 -0
  6. package/dist/src/ai/core.js +367 -0
  7. package/dist/src/ai/element-finder.js +409 -0
  8. package/{src → dist/src}/ai/index.js +35 -50
  9. package/dist/src/ai/page-analyzer.js +264 -0
  10. package/dist/src/ai/selector-healer.js +215 -0
  11. package/dist/src/index.js +116 -0
  12. package/dist/src/mcp/handlers/browser.js +230 -0
  13. package/dist/src/mcp/handlers/dom.js +550 -0
  14. package/dist/src/mcp/handlers/extract.js +451 -0
  15. package/dist/src/mcp/handlers/helpers.js +514 -0
  16. package/dist/src/mcp/handlers/index.js +63 -0
  17. package/dist/src/mcp/handlers/misc.js +1224 -0
  18. package/dist/src/mcp/handlers/network.js +1134 -0
  19. package/dist/src/mcp/handlers/state.js +215 -0
  20. package/dist/src/mcp/handlers/vision.js +475 -0
  21. package/dist/src/mcp/index.js +166 -0
  22. package/dist/src/mcp/server.js +117 -0
  23. package/{src → dist/src}/mcp/tools.js +12 -11
  24. package/dist/src/shared/tools.js +598 -0
  25. package/{test → dist/test}/cjs/test.js +119 -169
  26. package/dist/test/mcp/smoke-test.js +131 -0
  27. package/lib/esm/module/pageController.mjs +21 -18
  28. package/lib/esm/module/turnstile.mjs +7 -0
  29. package/package.json +22 -11
  30. package/.github/ISSUE_TEMPLATE/general_issue.yaml +0 -58
  31. package/.github/SETUP.md +0 -111
  32. package/.github/workflows/publish.yml +0 -162
  33. package/Dockerfile +0 -78
  34. package/lib/cjs/adblocker.bin +0 -0
  35. package/lib/cjs/index.js +0 -396
  36. package/src/ai/action-parser.js +0 -274
  37. package/src/ai/core.js +0 -379
  38. package/src/ai/element-finder.js +0 -466
  39. package/src/ai/page-analyzer.js +0 -295
  40. package/src/ai/selector-healer.js +0 -236
  41. package/src/index.js +0 -128
  42. package/src/mcp/handlers.js +0 -5397
  43. package/src/mcp/index.js +0 -190
  44. package/src/mcp/server.js +0 -141
  45. package/src/shared/tools.js +0 -667
  46. package/test/esm/test.mjs +0 -299
  47. package/test/mcp/smoke-test.js +0 -141
package/README.md CHANGED
@@ -139,9 +139,9 @@ Configure the server in your `opencode.jsonc` or standard MCP settings configura
139
139
 
140
140
  ---
141
141
 
142
- ## 🌐 Complete MCP Tool Reference (25 Tools)
142
+ ## 🌐 Complete MCP Tool Reference (23 Tools)
143
143
 
144
- The server exposes 25 highly optimized tools categorized into functional units:
144
+ The server exposes 23 highly optimized tools categorized into functional units:
145
145
 
146
146
  ### 🌐 Browser & Session
147
147
  | Tool Name | Description | Parameters |
@@ -185,11 +185,7 @@ The server exposes 25 highly optimized tools categorized into functional units:
185
185
  | `wait` | Smart delay with AI prediction or static timeout. | `duration` (number) |
186
186
  | `progress_tracker` | Track running automation progress with AI-estimated remaining times. | `step` (string), `percentage` (number) |
187
187
 
188
- ### 📸 Capture
189
- | Tool Name | Description | Parameters |
190
- |:---|:---|:---|
191
- | `screenshot` | Capture viewport, full page, or a specific element. Returns the image to the AI agent (base64) and can save to a file. | `fullPage` (boolean), `selector` (string), `format` (png/jpeg), `path` (string) |
192
- | `save_as_pdf` | Save the current page as a PDF via Chromium print-to-PDF (headless mode only). | `path` (string), `format` (string), `landscape` (boolean) |
188
+
193
189
 
194
190
  ### 👁️ AI Vision (Eyes)
195
191
  | Tool Name | Description | Parameters |
@@ -305,7 +301,7 @@ Run these scripts from the project root directory:
305
301
  | `npm run dev` | Alias to start the MCP server. |
306
302
  | `npm run mcp` | Start the MCP server. |
307
303
  | `npm run mcp:verbose` | Start the MCP server with verbose logging on `stderr`. |
308
- | `npm run list` | Clean list of all 25 tools with emojis and categories. |
304
+ | `npm run list` | Clean list of all 23 tools with emojis and categories. |
309
305
  | `npm run build` | Validate workspace structure and confirm library status. |
310
306
  | `npm test` | Execute the full test suite (CJS & ESM). |
311
307
  | `npm run cjs_test` | Run CommonJS test scripts. |
@@ -0,0 +1,384 @@
1
+ "use strict";
2
+ // @ts-nocheck
3
+ const { chromium } = require("patchright");
4
+ const { createCursor } = require("ghost-cursor-patchright");
5
+ const { PlaywrightBlocker } = require("@ghostery/adblocker-playwright");
6
+ const { pageController } = require("./module/pageController.js");
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+ let adBlockerInstance = null;
10
+ let adBlockerPromise = null;
11
+ function getAdBlocker() {
12
+ if (!adBlockerPromise) {
13
+ const cachePath = path.join(__dirname, 'adblocker.bin');
14
+ adBlockerPromise = PlaywrightBlocker.fromPrebuiltAdsAndTracking(fetch, {
15
+ path: cachePath,
16
+ read: fs.promises.readFile,
17
+ write: fs.promises.writeFile,
18
+ }).then(blocker => {
19
+ adBlockerInstance = blocker;
20
+ return blocker;
21
+ }).catch(err => {
22
+ console.error('[adblocker] Failed to initialize adblocker:', err.message);
23
+ return null;
24
+ });
25
+ }
26
+ return adBlockerPromise;
27
+ }
28
+ function loadEnvFile() {
29
+ const envPaths = [
30
+ path.join(process.cwd(), '.env'),
31
+ ];
32
+ let currentDir = process.cwd();
33
+ for (let i = 0; i < 5; i++) {
34
+ const envPath = path.join(currentDir, '.env');
35
+ if (fs.existsSync(envPath) && !envPaths.includes(envPath)) {
36
+ envPaths.push(envPath);
37
+ }
38
+ const parentDir = path.dirname(currentDir);
39
+ if (parentDir === currentDir)
40
+ break;
41
+ currentDir = parentDir;
42
+ }
43
+ for (const envPath of envPaths) {
44
+ try {
45
+ if (fs.existsSync(envPath)) {
46
+ const envContent = fs.readFileSync(envPath, 'utf-8');
47
+ envContent.split('\n').forEach(line => {
48
+ const trimmed = line.trim();
49
+ if (trimmed && !trimmed.startsWith('#')) {
50
+ const [key, ...valueParts] = trimmed.split('=');
51
+ const value = valueParts.join('=').replace(/^["']|["']$/g, '');
52
+ if (key && !process.env[key]) {
53
+ process.env[key] = value;
54
+ }
55
+ }
56
+ });
57
+ break;
58
+ }
59
+ }
60
+ catch (error) {
61
+ // Silently ignore .env loading errors
62
+ }
63
+ }
64
+ }
65
+ loadEnvFile();
66
+ function getDefaultHeadless() {
67
+ const envHeadless = process.env.HEADLESS;
68
+ if (envHeadless !== undefined && envHeadless !== null && envHeadless !== '') {
69
+ const value = envHeadless.toLowerCase().trim();
70
+ return value === 'true' || value === '1' || value === 'yes';
71
+ }
72
+ // Auto-detect CI environments
73
+ if (process.env.CI || process.env.GITHUB_ACTIONS || process.env.TRAVIS || process.env.CIRCLECI) {
74
+ return true;
75
+ }
76
+ // Auto-detect headless Linux environments without X11 or Wayland
77
+ if (process.platform === 'linux') {
78
+ const hasDisplay = process.env.DISPLAY || process.env.WAYLAND_DISPLAY;
79
+ if (!hasDisplay) {
80
+ return true;
81
+ }
82
+ }
83
+ return false;
84
+ }
85
+ function setupRealPage(browser, page) {
86
+ if (page._setupApplied)
87
+ return page;
88
+ page._setupApplied = true;
89
+ // Enable ad blocker
90
+ if (adBlockerInstance) {
91
+ adBlockerInstance.enableBlockingInPage(page).catch(() => { });
92
+ }
93
+ else {
94
+ getAdBlocker().then(blocker => {
95
+ if (blocker) {
96
+ blocker.enableBlockingInPage(page).catch(() => { });
97
+ }
98
+ });
99
+ }
100
+ // Human-like smooth scrolling with 60FPS Cubic Ease-Out physics
101
+ page.realScroll = async (deltaY, duration = 600) => {
102
+ try {
103
+ const stepDelay = 15; // ~60 FPS
104
+ const steps = Math.max(10, Math.floor(duration / stepDelay));
105
+ const easeOutCubic = (t) => 1 - Math.pow(1 - t, 3);
106
+ let currentScroll = 0;
107
+ for (let i = 1; i <= steps; i++) {
108
+ const t = i / steps;
109
+ const targetScroll = deltaY * easeOutCubic(t);
110
+ const diff = targetScroll - currentScroll;
111
+ await page.mouse.wheel(0, diff);
112
+ currentScroll = targetScroll;
113
+ await new Promise(r => setTimeout(r, stepDelay));
114
+ }
115
+ }
116
+ catch (e) {
117
+ // Fallback to native window scroll in case of wheel errors
118
+ try {
119
+ await page.evaluate((y) => window.scrollBy({ top: y, behavior: 'smooth' }), deltaY);
120
+ }
121
+ catch (_) { }
122
+ }
123
+ };
124
+ // Ghost Cursor integration - Bézier curve human-like mouse movement
125
+ try {
126
+ const cursor = createCursor(page);
127
+ page.realCursor = {
128
+ move: async (selector, options = {}) => {
129
+ try {
130
+ await cursor.actions.move(selector, options);
131
+ }
132
+ catch (e) {
133
+ // Fallback to native hover if ghost-cursor fails
134
+ try {
135
+ await page.hover(selector);
136
+ }
137
+ catch (_) { }
138
+ }
139
+ }
140
+ };
141
+ page.realClick = async (selector, options = {}) => {
142
+ try {
143
+ await cursor.actions.click({ target: selector, ...options });
144
+ }
145
+ catch (e) {
146
+ // Fallback to native click if ghost-cursor fails
147
+ await page.click(selector, options);
148
+ }
149
+ };
150
+ }
151
+ catch (e) {
152
+ // Fallback if ghost-cursor-patchright fails to initialize
153
+ if (!page.realClick) {
154
+ page.realClick = async (selector, options) => {
155
+ await page.click(selector, options);
156
+ };
157
+ }
158
+ if (!page.realCursor) {
159
+ page.realCursor = {
160
+ move: async (selector) => {
161
+ try {
162
+ await page.hover(selector);
163
+ }
164
+ catch (_) { }
165
+ }
166
+ };
167
+ }
168
+ }
169
+ return page;
170
+ }
171
+ function getBraveExecutablePath() {
172
+ if (process.env.BRAVE_PATH && fs.existsSync(process.env.BRAVE_PATH)) {
173
+ return process.env.BRAVE_PATH;
174
+ }
175
+ const platform = process.platform;
176
+ const { execSync } = require('child_process');
177
+ // Try automatic scanning via CLI / registry query
178
+ if (platform === 'win32') {
179
+ const regQueries = [
180
+ 'reg query "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\brave.exe" /ve',
181
+ 'reg query "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\brave.exe" /ve',
182
+ 'reg query "HKEY_LOCAL_MACHINE\\SOFTWARE\\Clients\\StartMenuInternet\\Brave-Browser\\shell\\open\\command" /ve'
183
+ ];
184
+ for (const cmd of regQueries) {
185
+ try {
186
+ const output = execSync(cmd, { stdio: ['ignore', 'pipe', 'ignore'] }).toString();
187
+ const match = output.match(/REG_SZ\s+(.*)/);
188
+ if (match && match[1]) {
189
+ let p = match[1].trim().replace(/^"|"$/g, '');
190
+ if (!p.toLowerCase().endsWith('.exe')) {
191
+ const exeIndex = p.toLowerCase().indexOf('.exe');
192
+ if (exeIndex !== -1) {
193
+ p = p.substring(0, exeIndex + 4).replace(/^"|"$/g, '');
194
+ }
195
+ }
196
+ if (fs.existsSync(p))
197
+ return p;
198
+ }
199
+ }
200
+ catch (e) { }
201
+ }
202
+ try {
203
+ const output = execSync('where brave.exe', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim().split('\r\n')[0];
204
+ if (output && fs.existsSync(output))
205
+ return output;
206
+ }
207
+ catch (e) { }
208
+ }
209
+ else if (platform === 'darwin') {
210
+ try {
211
+ const output = execSync('mdfind "kMDItemCFBundleIdentifier == \'com.brave.Browser\'"', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim().split('\n')[0];
212
+ if (output) {
213
+ const p = path.join(output, 'Contents', 'MacOS', 'Brave Browser');
214
+ if (fs.existsSync(p))
215
+ return p;
216
+ }
217
+ }
218
+ catch (e) { }
219
+ }
220
+ else {
221
+ try {
222
+ const output = execSync('which brave-browser || which brave', { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
223
+ if (output && fs.existsSync(output))
224
+ return output;
225
+ }
226
+ catch (e) { }
227
+ }
228
+ // Fallback to hardcoded common paths
229
+ let paths = [];
230
+ if (platform === 'win32') {
231
+ paths = [
232
+ path.join(process.env.PROGRAMFILES || 'C:\\Program Files', 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
233
+ path.join(process.env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)', 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
234
+ path.join(process.env.LOCALAPPDATA || '', 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe')
235
+ ].filter(p => p);
236
+ }
237
+ else if (platform === 'darwin') {
238
+ paths = [
239
+ '/Applications/Brave Browser.app/Contents/MacOS/Brave Browser'
240
+ ];
241
+ }
242
+ else {
243
+ paths = [
244
+ '/usr/bin/brave-browser',
245
+ '/usr/bin/brave',
246
+ '/usr/bin/brave-browser-stable',
247
+ '/usr/bin/brave-browser-beta',
248
+ '/usr/bin/brave-browser-nightly',
249
+ '/usr/local/bin/brave-browser',
250
+ '/usr/local/bin/brave'
251
+ ];
252
+ }
253
+ for (const p of paths) {
254
+ if (p && fs.existsSync(p)) {
255
+ return p;
256
+ }
257
+ }
258
+ return null;
259
+ }
260
+ async function applyUserAgentOverride(page, userAgent, userAgentMetadata) {
261
+ try {
262
+ const client = await page.context().newCDPSession(page);
263
+ await client.send('Emulation.setUserAgentOverride', {
264
+ userAgent: userAgent,
265
+ userAgentMetadata: userAgentMetadata
266
+ });
267
+ }
268
+ catch (e) {
269
+ // Ignore errors
270
+ }
271
+ }
272
+ async function connect({ args = [], headless = getDefaultHeadless(), proxy = {}, turnstile = false, executablePath = undefined, } = {}) {
273
+ let playwrightProxy = undefined;
274
+ if (proxy && proxy.host && proxy.port) {
275
+ playwrightProxy = {
276
+ server: `${proxy.host}:${proxy.port}`
277
+ };
278
+ if (proxy.username && proxy.password) {
279
+ playwrightProxy.username = proxy.username;
280
+ playwrightProxy.password = proxy.password;
281
+ }
282
+ }
283
+ // 1. Launch a temporary browser to retrieve the native user agent and properties
284
+ const tempBrowser = await chromium.launch({
285
+ headless: true,
286
+ args: ['--no-sandbox', '--disable-setuid-sandbox'],
287
+ ...(executablePath ? { executablePath } : {}),
288
+ });
289
+ const tempContext = await tempBrowser.newContext();
290
+ const tempPage = await tempContext.newPage();
291
+ let nativeUa = '';
292
+ let isBrave = false;
293
+ try {
294
+ nativeUa = await tempPage.evaluate(() => navigator.userAgent);
295
+ isBrave = await tempPage.evaluate(() => typeof navigator.brave !== 'undefined');
296
+ }
297
+ catch (e) {
298
+ nativeUa = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/148.0.0.0 Safari/537.36';
299
+ isBrave = executablePath && executablePath.toLowerCase().includes('brave');
300
+ }
301
+ await tempBrowser.close();
302
+ let modifiedUa = nativeUa.replace(/HeadlessChrome\//g, 'Chrome/');
303
+ const chromeVersionMatch = modifiedUa.match(/Chrome\/([\d.]+)/);
304
+ const chromeVersion = chromeVersionMatch ? chromeVersionMatch[1] : '148.0.0.0';
305
+ const majorVersion = chromeVersion.split('.')[0];
306
+ const brands = [
307
+ { brand: 'Chromium', version: majorVersion },
308
+ { brand: 'Not/A)Brand', version: '99' }
309
+ ];
310
+ if (isBrave) {
311
+ brands.unshift({ brand: 'Brave', version: majorVersion });
312
+ }
313
+ else {
314
+ brands.unshift({ brand: 'Google Chrome', version: majorVersion });
315
+ }
316
+ let platformName = 'Windows';
317
+ if (nativeUa.includes('Macintosh') || nativeUa.includes('Mac OS X')) {
318
+ platformName = 'macOS';
319
+ }
320
+ else if (nativeUa.includes('Linux')) {
321
+ platformName = 'Linux';
322
+ }
323
+ const userAgentMetadata = {
324
+ brands: brands,
325
+ mobile: false,
326
+ platform: platformName,
327
+ platformVersion: platformName === 'macOS' ? '14.0.0' : platformName === 'Linux' ? '6.0.0' : '10.0.0',
328
+ architecture: 'x86',
329
+ model: '',
330
+ bitness: '64',
331
+ wow64: false
332
+ };
333
+ const chromiumArgs = [
334
+ `--user-agent=${modifiedUa}`,
335
+ '--disable-blink-features=AutomationControlled',
336
+ '--no-sandbox',
337
+ '--disable-setuid-sandbox',
338
+ ...args
339
+ ];
340
+ // If headless is true, we run with headless: false but pass '--headless=new' to args.
341
+ // This triggers Chromium's modern undetected headless mode instead of Playwright's default old headless shell.
342
+ let launchHeadless = headless;
343
+ if (headless === true) {
344
+ launchHeadless = false;
345
+ if (!chromiumArgs.includes('--headless=new')) {
346
+ chromiumArgs.push('--headless=new');
347
+ }
348
+ }
349
+ const browser = await chromium.launch({
350
+ headless: launchHeadless,
351
+ args: chromiumArgs,
352
+ proxy: playwrightProxy,
353
+ ...(executablePath ? { executablePath } : {}),
354
+ });
355
+ // Ensure ad blocker is ready
356
+ await getAdBlocker();
357
+ const context = await browser.newContext({
358
+ viewport: null,
359
+ });
360
+ let page = await context.newPage();
361
+ await applyUserAgentOverride(page, modifiedUa, userAgentMetadata);
362
+ setupRealPage(browser, page);
363
+ page = await pageController({
364
+ browser,
365
+ page,
366
+ proxy,
367
+ turnstile,
368
+ });
369
+ context.on('page', async (newPage) => {
370
+ await applyUserAgentOverride(newPage, modifiedUa, userAgentMetadata);
371
+ setupRealPage(browser, newPage);
372
+ await pageController({
373
+ browser,
374
+ page: newPage,
375
+ proxy,
376
+ turnstile,
377
+ });
378
+ });
379
+ return {
380
+ browser,
381
+ page,
382
+ };
383
+ }
384
+ module.exports = { connect };
@@ -1,15 +1,14 @@
1
+ "use strict";
2
+ // @ts-nocheck
1
3
  const { checkTurnstile } = require('./turnstile.js');
2
-
3
4
  async function pageController({ browser, page, proxy, turnstile }) {
4
- if (page._pageControllerApplied) return page;
5
+ if (page._pageControllerApplied)
6
+ return page;
5
7
  page._pageControllerApplied = true;
6
-
7
8
  let solveStatus = turnstile;
8
-
9
9
  page.on('close', () => {
10
10
  solveStatus = false;
11
11
  });
12
-
13
12
  async function turnstileSolver() {
14
13
  while (solveStatus) {
15
14
  await checkTurnstile({ page }).catch(() => { });
@@ -17,34 +16,35 @@ async function pageController({ browser, page, proxy, turnstile }) {
17
16
  }
18
17
  return;
19
18
  }
20
-
21
19
  if (solveStatus) {
22
20
  turnstileSolver();
23
21
  }
24
-
25
22
  // === POPUP AD BLOCKING ===
26
23
  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));
24
+ if (!context._popupBlockerApplied) {
25
+ context._popupBlockerApplied = true;
26
+ context.on('page', async (newPage) => {
27
+ try {
28
+ const opener = await newPage.opener();
29
+ if (opener) {
30
+ const url = newPage.url();
31
+ const isAdPopup = url === 'about:blank' ||
32
+ url.includes('ad') ||
33
+ url.includes('pop') ||
34
+ url.includes('click') ||
35
+ url.includes('redirect') ||
36
+ url.includes('track');
37
+ if (isAdPopup) {
38
+ await newPage.close().catch(() => { });
39
+ console.error('[popup-blocker] Blocked popup ad:', url.substring(0, 50));
40
+ }
41
41
  }
42
42
  }
43
- } catch (e) {
44
- // Ignore errors
45
- }
46
- });
47
-
43
+ catch (e) {
44
+ // Ignore errors
45
+ }
46
+ });
47
+ }
48
48
  // NOTE: JS stealth overrides are commented out because Patchright natively handles automation hiding.
49
49
  // Manual JS overrides trigger Pixelscan fingerprint masking detectors.
50
50
  /*
@@ -63,8 +63,6 @@ async function pageController({ browser, page, proxy, turnstile }) {
63
63
  }
64
64
  });
65
65
  */
66
-
67
66
  return page;
68
67
  }
69
-
70
- module.exports = { pageController };
68
+ module.exports = { pageController };
@@ -1,7 +1,16 @@
1
+ "use strict";
2
+ // @ts-nocheck
1
3
  const checkTurnstile = async ({ page }) => {
2
4
  try {
3
5
  const elements = await page.locator('[name="cf-turnstile-response"]').all();
4
6
  if (elements.length <= 0) {
7
+ const isChallenge = await page.evaluate(() => {
8
+ return document.title.includes('Just a moment') ||
9
+ document.querySelector('#challenge-stage') !== null ||
10
+ document.querySelector('.cf-turnstile') !== null;
11
+ });
12
+ if (!isChallenge)
13
+ return false;
5
14
  const coordinates = await page.evaluate(() => {
6
15
  let coordinates = [];
7
16
  document.querySelectorAll('div').forEach(item => {
@@ -11,9 +20,9 @@ const checkTurnstile = async ({ page }) => {
11
20
  if (itemCss.margin == "0px" && itemCss.padding == "0px" && itemCoordinates.width > 290 && itemCoordinates.width <= 310 && !item.querySelector('*')) {
12
21
  coordinates.push({ x: itemCoordinates.x, y: item.getBoundingClientRect().y, w: item.getBoundingClientRect().width, h: item.getBoundingClientRect().height });
13
22
  }
14
- } catch (err) { }
23
+ }
24
+ catch (err) { }
15
25
  });
16
-
17
26
  if (coordinates.length <= 0) {
18
27
  document.querySelectorAll('div').forEach(item => {
19
28
  try {
@@ -21,28 +30,29 @@ const checkTurnstile = async ({ page }) => {
21
30
  if (itemCoordinates.width > 290 && itemCoordinates.width <= 310 && !item.querySelector('*')) {
22
31
  coordinates.push({ x: itemCoordinates.x, y: item.getBoundingClientRect().y, w: item.getBoundingClientRect().width, h: item.getBoundingClientRect().height });
23
32
  }
24
- } catch (err) { }
33
+ }
34
+ catch (err) { }
25
35
  });
26
36
  }
27
37
  return coordinates;
28
38
  });
29
-
30
39
  for (const item of coordinates) {
31
40
  try {
32
41
  let x = item.x + 30;
33
42
  let y = item.y + item.h / 2;
34
43
  await page.mouse.click(x, y);
35
- } catch (err) { }
44
+ }
45
+ catch (err) { }
36
46
  }
37
47
  return true;
38
48
  }
39
-
40
49
  for (const element of elements) {
41
50
  try {
42
51
  // Get the parent element bounding box
43
52
  const box = await element.evaluate(el => {
44
53
  const parent = el.parentElement;
45
- if (!parent) return null;
54
+ if (!parent)
55
+ return null;
46
56
  const rect = parent.getBoundingClientRect();
47
57
  return { x: rect.x, y: rect.y, width: rect.width, height: rect.height };
48
58
  });
@@ -51,12 +61,13 @@ const checkTurnstile = async ({ page }) => {
51
61
  let y = box.y + box.height / 2;
52
62
  await page.mouse.click(x, y);
53
63
  }
54
- } catch (err) { }
64
+ }
65
+ catch (err) { }
55
66
  }
56
67
  return true;
57
- } catch (err) {
68
+ }
69
+ catch (err) {
58
70
  return false;
59
71
  }
60
- }
61
-
62
- module.exports = { checkTurnstile };
72
+ };
73
+ module.exports = { checkTurnstile };