KiWA001 commited on
Commit
3db3e6c
·
1 Parent(s): 531eb24

Add enhanced stealth and immediate screenshot after click

Browse files

- Added comprehensive browser stealth techniques:
* Enhanced browser launch args to avoid automation detection
* More realistic user agent (Mac instead of Windows)
* Added locale, timezone, geolocation
* Comprehensive JavaScript overrides for navigator properties
* Chrome runtime object simulation
* WebGL vendor spoofing

- Added iframe detection and clicking:
* CAPTCHA is often inside cross-origin iframes
* _try_click_iframe method detects and clicks inside iframes
* Calculates relative coordinates within iframe

- Immediate screenshot after click:
* Screenshot taken 1 second after each click
* No need to wait for 3-second auto-refresh
* Shows result of your action immediately
* Also takes screenshot on error for debugging

This should help with CAPTCHA checkbox clicking!

Files changed (1) hide show
  1. copilot_portal.py +145 -8
copilot_portal.py CHANGED
@@ -37,32 +37,121 @@ class CopilotPortal:
37
  return False
38
 
39
  async def initialize(self):
40
- """Initialize the browser and navigate to Copilot."""
41
  if self.is_initialized:
42
  return
43
 
44
  try:
45
- logger.info("🚀 Portal: Launching browser...")
46
  self.playwright = await async_playwright().start()
47
 
 
48
  self.browser = await self.playwright.chromium.launch(
49
- headless=True, # Headless but we'll screenshot it
50
  args=[
51
  "--disable-blink-features=AutomationControlled",
 
 
52
  "--no-sandbox",
53
  "--disable-dev-shm-usage",
54
  "--disable-gpu",
 
 
 
 
 
 
 
 
 
55
  ],
56
  )
57
 
 
58
  self.context = await self.browser.new_context(
59
  viewport={"width": 1280, "height": 800},
60
- user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
 
 
 
 
 
 
61
  )
62
 
63
- # Hide automation
64
  await self.context.add_init_script("""
 
65
  Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  """)
67
 
68
  self.page = await self.context.new_page()
@@ -203,19 +292,67 @@ class CopilotPortal:
203
  logger.error(f"Refresh error: {e}")
204
 
205
  async def click_at_coordinates(self, x: float, y: float):
206
- """Click at specific coordinates on the page."""
207
  if not self.page:
208
  logger.error("Portal: No page available for click")
209
  return
210
 
211
  try:
212
  logger.info(f"Portal: Clicking at coordinates ({x}, {y})")
 
 
213
  await self.page.mouse.click(x, y)
214
- await asyncio.sleep(1) # Wait for any response
 
 
 
 
 
 
 
 
 
 
 
 
 
215
  await self.take_screenshot()
216
- logger.info("Portal: Click completed")
 
217
  except Exception as e:
218
  logger.error(f"Portal click error: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219
 
220
  async def close(self):
221
  """Close the browser."""
 
37
  return False
38
 
39
  async def initialize(self):
40
+ """Initialize the browser and navigate to Copilot with enhanced stealth."""
41
  if self.is_initialized:
42
  return
43
 
44
  try:
45
+ logger.info("🚀 Portal: Launching browser with stealth...")
46
  self.playwright = await async_playwright().start()
47
 
48
+ # Enhanced browser args for stealth
49
  self.browser = await self.playwright.chromium.launch(
50
+ headless=True,
51
  args=[
52
  "--disable-blink-features=AutomationControlled",
53
+ "--disable-features=IsolateOrigins,site-per-process",
54
+ "--disable-site-isolation-trials",
55
  "--no-sandbox",
56
  "--disable-dev-shm-usage",
57
  "--disable-gpu",
58
+ "--disable-web-security",
59
+ "--disable-features=BlockInsecurePrivateNetworkRequests",
60
+ "--disable-features=InterestCohort",
61
+ "--window-size=1280,800",
62
+ "--start-maximized",
63
+ "--force-color-profile=srgb",
64
+ "--disable-background-timer-throttling",
65
+ "--disable-backgrounding-occluded-windows",
66
+ "--disable-renderer-backgrounding",
67
  ],
68
  )
69
 
70
+ # More realistic browser context
71
  self.context = await self.browser.new_context(
72
  viewport={"width": 1280, "height": 800},
73
+ user_agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
74
+ locale="en-US",
75
+ timezone_id="America/New_York",
76
+ geolocation={"latitude": 40.7128, "longitude": -74.0060}, # NYC
77
+ permissions=["geolocation"],
78
+ color_scheme="light",
79
+ reduced_motion="no-preference",
80
  )
81
 
82
+ # Enhanced stealth script
83
  await self.context.add_init_script("""
84
+ // Override navigator properties
85
  Object.defineProperty(navigator, 'webdriver', {get: () => undefined});
86
+ Object.defineProperty(navigator, 'plugins', {get: () => [1, 2, 3, 4, 5]});
87
+ Object.defineProperty(navigator, 'languages', {get: () => ['en-US', 'en']});
88
+
89
+ // Override permissions
90
+ const originalQuery = window.navigator.permissions.query;
91
+ window.navigator.permissions.query = (parameters) => (
92
+ parameters.name === 'notifications' ?
93
+ Promise.resolve({ state: Notification.permission }) :
94
+ originalQuery(parameters)
95
+ );
96
+
97
+ // Add Chrome runtime
98
+ window.chrome = {
99
+ runtime: {
100
+ OnInstalledReason: {
101
+ CHROME_UPDATE: "chrome_update",
102
+ INSTALL: "install",
103
+ SHARED_MODULE_UPDATE: "shared_module_update",
104
+ UPDATE: "update"
105
+ },
106
+ OnRestartRequiredReason: {
107
+ APP_UPDATE: "app_update",
108
+ OS_UPDATE: "os_update",
109
+ PERIODIC: "periodic"
110
+ },
111
+ PlatformArch: {
112
+ ARM: "arm",
113
+ ARM64: "arm64",
114
+ MIPS: "mips",
115
+ MIPS64: "mips64",
116
+ X86_32: "x86-32",
117
+ X86_64: "x86-64"
118
+ },
119
+ PlatformNaclArch: {
120
+ ARM: "arm",
121
+ MIPS: "mips",
122
+ MIPS64: "mips64",
123
+ MIPS64_EL: "mips64el",
124
+ ARM64: "arm64",
125
+ X86_32: "x86-32",
126
+ X86_64: "x86-64"
127
+ },
128
+ PlatformOs: {
129
+ ANDROID: "android",
130
+ CROS: "cros",
131
+ LINUX: "linux",
132
+ MAC: "mac",
133
+ OPENBSD: "openbsd",
134
+ WIN: "win"
135
+ },
136
+ RequestUpdateCheckStatus: {
137
+ NO_UPDATE: "no_update",
138
+ THROTTLED: "throttled",
139
+ UPDATE_AVAILABLE: "update_available"
140
+ }
141
+ }
142
+ };
143
+
144
+ // Override WebGL
145
+ const getParameter = WebGLRenderingContext.prototype.getParameter;
146
+ WebGLRenderingContext.prototype.getParameter = function(parameter) {
147
+ if (parameter === 37445) {
148
+ return 'Intel Inc.';
149
+ }
150
+ if (parameter === 37446) {
151
+ return 'Intel Iris OpenGL Engine';
152
+ }
153
+ return getParameter(parameter);
154
+ };
155
  """)
156
 
157
  self.page = await self.context.new_page()
 
292
  logger.error(f"Refresh error: {e}")
293
 
294
  async def click_at_coordinates(self, x: float, y: float):
295
+ """Click at specific coordinates on the page and immediately take screenshot."""
296
  if not self.page:
297
  logger.error("Portal: No page available for click")
298
  return
299
 
300
  try:
301
  logger.info(f"Portal: Clicking at coordinates ({x}, {y})")
302
+
303
+ # First try clicking on main page
304
  await self.page.mouse.click(x, y)
305
+
306
+ # Wait a short moment for the page to react
307
+ await asyncio.sleep(0.5)
308
+
309
+ # Check if there's an iframe at that location (CAPTCHA is often in iframe)
310
+ iframe_clicked = await self._try_click_iframe(x, y)
311
+
312
+ if iframe_clicked:
313
+ logger.info("Portal: Clicked inside iframe")
314
+
315
+ # Wait a bit more for any CAPTCHA processing
316
+ await asyncio.sleep(1)
317
+
318
+ # Take screenshot immediately
319
  await self.take_screenshot()
320
+ logger.info("Portal: Click completed, screenshot taken")
321
+
322
  except Exception as e:
323
  logger.error(f"Portal click error: {e}")
324
+ # Still try to take screenshot on error
325
+ try:
326
+ await self.take_screenshot()
327
+ except:
328
+ pass
329
+
330
+ async def _try_click_iframe(self, x: float, y: float) -> bool:
331
+ """Try to click inside iframes at the given coordinates."""
332
+ try:
333
+ # Get all iframes
334
+ iframes = await self.page.query_selector_all('iframe')
335
+
336
+ for iframe in iframes:
337
+ try:
338
+ # Check if iframe is visible and contains the coordinates
339
+ box = await iframe.bounding_box()
340
+ if box and box['x'] <= x <= box['x'] + box['width'] and box['y'] <= y <= box['y'] + box['height']:
341
+ # Click inside the iframe
342
+ frame = await iframe.content_frame()
343
+ if frame:
344
+ # Calculate relative coordinates
345
+ rel_x = x - box['x']
346
+ rel_y = y - box['y']
347
+ await frame.mouse.click(rel_x, rel_y)
348
+ return True
349
+ except:
350
+ continue
351
+
352
+ return False
353
+ except Exception as e:
354
+ logger.error(f"Iframe click error: {e}")
355
+ return False
356
 
357
  async def close(self):
358
  """Close the browser."""