@@ -76,44 +76,51 @@ def get_tracks(self, distance):
7676 :return: List of x-offsets
7777 """
7878 tracks = []
79- current = 0
79+ current = 0 # Integer current position
8080 mid = distance * 4 / 5 # Decelerate after 4/5
81- t = 0.2 # Time interval
81+ t = 0.02 # Time interval (simulated) - reduced for smoother steps
8282 v = 0 # Initial velocity
8383
8484 while current < distance :
8585 if current < mid :
8686 # Acceleration phase
87- a = 2 + random .uniform ( - 0.5 , 0.5 )
87+ a = random .randint ( 400 , 600 )
8888 else :
8989 # Deceleration phase
90- a = - 3 + random .uniform ( - 0.5 , 0.5 )
90+ a = - random .randint ( 600 , 800 )
9191
9292 v0 = v
9393 v = v0 + a * t
9494 move = v0 * t + 0.5 * a * t * t
95- current += move
96- tracks .append (round (move ))
97-
98- # Correction
99- generated_distance = sum (tracks )
100- diff = distance - generated_distance
101- if diff != 0 :
102- tracks .append (diff )
10395
96+ # Ensure forward movement
97+ if move < 1 : move = 1
98+
99+ move_int = round (move )
100+
101+ # Check if this move overshoots
102+ if current + move_int > distance :
103+ move_int = distance - current
104+
105+ tracks .append (move_int )
106+ current += move_int
107+
108+ if current >= distance :
109+ break
110+
104111 return tracks
105112
106113 def get_tracks_with_jitter (self , distance ):
107114 """
108- Advanced tracks with Y-axis jitter and overshoot
115+ Advanced tracks with Y-axis jitter
109116 """
110117 tracks = self .get_tracks (distance )
111118
112- # Simulate overshoot
113- if random .choice ([True , False ]):
114- overshoot = random .randint (2 , 5 )
115- tracks .append (overshoot )
116- tracks .append (- overshoot )
119+ # Overshoot removed based on user feedback (causes validation failure)
120+ # if random.choice([True, False]):
121+ # overshoot = random.randint(2, 5)
122+ # tracks.append(overshoot)
123+ # tracks.append(-overshoot)
117124
118125 return tracks
119126
@@ -206,6 +213,11 @@ def init_driver(self):
206213 options .add_argument ('--disable-dev-shm-usage' )
207214 options .add_argument ("--window-size=1920,1080" )
208215
216+ # Anti-detection options
217+ options .add_argument ("--disable-blink-features=AutomationControlled" )
218+ options .add_experimental_option ("excludeSwitches" , ["enable-automation" ])
219+ options .add_experimental_option ('useAutomationExtension' , False )
220+
209221 chrome_binary = os .getenv ("CHROME_BINARY_PATH" )
210222 # Fallback for Docker if env var is empty (overridden by .env)
211223 if not chrome_binary and os .environ .get ('PYTHON_IN_DOCKER' ) == 'true' :
@@ -226,6 +238,16 @@ def init_driver(self):
226238 service = ChromeService (driver_path )
227239
228240 driver = webdriver .Chrome (options = options , service = service )
241+
242+ # CDP command to hide webdriver property
243+ driver .execute_cdp_cmd ("Page.addScriptToEvaluateOnNewDocument" , {
244+ "source" : """
245+ Object.defineProperty(navigator, 'webdriver', {
246+ get: () => undefined
247+ })
248+ """
249+ })
250+
229251 driver .implicitly_wait (self .wait_time )
230252 return driver
231253
@@ -301,7 +323,8 @@ def perform_login(self, driver):
301323 base64_img = driver .execute_script (js_img )
302324
303325 # Get rendered width (CSS width)
304- js_width = 'return document.getElementById("slideVerify").childNodes[0].clientWidth;'
326+ # Get rendered width (CSS width) - Use getBoundingClientRect for float precision
327+ js_width = 'return document.getElementById("slideVerify").childNodes[0].getBoundingClientRect().width;'
305328 rendered_width = driver .execute_script (js_width )
306329
307330 img_data = base64_img .split (',' )[1 ]
@@ -312,34 +335,34 @@ def perform_login(self, driver):
312335
313336 gap_pos = self .resolver .solve_gap (image )
314337
315- # Apply scaling
316- final_distance = int (gap_pos * scale_factor )
338+ # Apply scaling and round to nearest integer
339+ final_distance = int (round ( gap_pos * scale_factor ) )
317340
318341 # Apply manual offset
319- slider_offset = int (os .getenv ("SLIDER_OFFSET" , 2 ))
342+ slider_offset = int (os .getenv ("SLIDER_OFFSET" , 5 ))
320343 final_distance += slider_offset
321344
322345 logging .info (f"Captcha: Gap={ gap_pos } , Scale={ scale_factor :.2f} , Offset={ slider_offset } , FinalDist={ final_distance } " )
323346
324347 # Save debug image with lines
325- try :
326- from PIL import ImageDraw
327- debug_img = image .copy ()
328- draw = ImageDraw .Draw (debug_img )
348+ # try:
349+ # from PIL import ImageDraw
350+ # debug_img = image.copy()
351+ # draw = ImageDraw.Draw(debug_img)
329352
330- # Red line: Original VLM detection
331- draw .line ([(gap_pos , 0 ), (gap_pos , debug_img .height )], fill = "red" , width = 3 )
353+ # # Red line: Original VLM detection
354+ # draw.line([(gap_pos, 0), (gap_pos, debug_img.height)], fill="red", width=3)
332355
333- # Green line: Final target (converted back to image scale)
334- final_target_on_image = int (final_distance / scale_factor )
335- draw .line ([(final_target_on_image , 0 ), (final_target_on_image , debug_img .height )], fill = "green" , width = 3 )
356+ # # Green line: Final target (converted back to image scale)
357+ # final_target_on_image = int(final_distance / scale_factor)
358+ # draw.line([(final_target_on_image, 0), (final_target_on_image, debug_img.height)], fill="green", width=3)
336359
337- timestamp = time .strftime ("%Y%m%d_%H%M%S" )
338- debug_path = f"./errors/captcha_{ timestamp } .png"
339- debug_img .save (debug_path )
340- logging .info (f"Saved debug captcha image to { debug_path } " )
341- except Exception as e :
342- logging .warning (f"Failed to save debug image: { e } " )
360+ # timestamp = time.strftime("%Y%m%d_%H%M%S")
361+ # debug_path = f"./errors/captcha_{timestamp}.png"
362+ # debug_img.save(debug_path)
363+ # logging.info(f"Saved debug captcha image to {debug_path}")
364+ # except Exception as e:
365+ # logging.warning(f"Failed to save debug image: {e}")
343366
344367 self .simulate_slide (driver , final_distance )
345368 time .sleep (self .retry_delay )
@@ -348,13 +371,13 @@ def perform_login(self, driver):
348371 logging .info (f"Login failed (Attempt { attempt } ), retrying captcha..." )
349372
350373 # Capture screenshot to see the error message
351- try :
352- timestamp = time .strftime ("%Y%m%d_%H%M%S" )
353- error_shot_path = f"./errors/login_fail_{ timestamp } .png"
354- driver .save_screenshot (error_shot_path )
355- logging .info (f"Saved login failure screenshot to { error_shot_path } " )
356- except Exception as e :
357- logging .warning (f"Failed to save failure screenshot: { e } " )
374+ # try:
375+ # timestamp = time.strftime("%Y%m%d_%H%M%S")
376+ # error_shot_path = f"./errors/login_fail_{timestamp}.png"
377+ # driver.save_screenshot(error_shot_path)
378+ # logging.info(f"Saved login failure screenshot to {error_shot_path}")
379+ # except Exception as e:
380+ # logging.warning(f"Failed to save failure screenshot: {e}")
358381
359382 self ._click_element (driver , By .CLASS_NAME , "el-button.el-button--primary" )
360383 time .sleep (self .retry_delay * 2 )
@@ -369,19 +392,31 @@ def run(self):
369392 # Force window size for headless mode
370393 driver .set_window_size (1920 , 1080 )
371394 size = driver .get_window_size ()
372- logging .info (f"Driver initialized. Window size: { size } " )
395+ pixel_ratio = driver .execute_script ("return window.devicePixelRatio;" )
396+ logging .info (f"Driver initialized. Window size: { size } , DevicePixelRatio: { pixel_ratio } " )
397+
398+ # Start Screen Recording
399+ # from recorder import ScreenRecorder
400+ # timestamp = time.strftime("%Y%m%d_%H%M%S")
401+ # video_path = f"./errors/record_{timestamp}.avi"
402+ # recorder = ScreenRecorder(driver, video_path, fps=3.0)
403+ # recorder.start()
373404
374405 publisher = MQTTPublisher ()
375406
376407 try :
377408 if self .perform_login (driver ):
378409 logging .info ("Login successful!" )
410+ # Stop recording immediately after success to save time/space
411+ # recorder.stop()
379412 else :
380413 logging .error ("Login failed!" )
414+ # recorder.stop()
381415 driver .quit ()
382416 return
383417 except Exception as e :
384418 logging .error (f"Login exception: { e } " )
419+ # recorder.stop()
385420 driver .quit ()
386421 return
387422
@@ -410,6 +445,7 @@ def run(self):
410445
411446 logging .info ("All tasks completed successfully." )
412447 self .cleanup_debug_images ()
448+ # recorder.stop()
413449 driver .quit ()
414450
415451 def cleanup_debug_images (self ):
0 commit comments