@@ -127,10 +127,14 @@ async def is_bypassed(self, page) -> bool:
127127 """Check if Cloudflare challenge has been bypassed."""
128128 try :
129129 title = await page .title ()
130+ if "just a moment" in title .lower ():
131+ return False
130132 html_content = await page .content ()
131- return "just a moment" not in title .lower () and "please complete the captcha" not in html_content .lower ()
133+ if "please complete the captcha" in html_content .lower ():
134+ return False
135+ return True
132136 except Exception as e :
133- self .log_message (f"Error checking page title : { e } " )
137+ self .log_message (f"Error checking bypass status : { e } " )
134138 return False
135139
136140 async def determine_challenge_type (self , page ) -> CaptchaType :
@@ -153,45 +157,66 @@ async def solve_cloudflare_challenge(self, url: str, page) -> bool:
153157 try :
154158 # Navigate to the target URL
155159 self .log_message (f"Navigating to { url } " )
156- await page .goto (url , wait_until = "networkidle" , timeout = 10000 )
160+ try :
161+ await page .goto (url , wait_until = "domcontentloaded" , timeout = 30000 )
162+ except Exception as nav_err :
163+ self .log_message (f"Navigation warning: { nav_err } " )
164+ # Wait for challenge scripts to load and execute
157165 await asyncio .sleep (5 )
158- html_content = await page .content ()
159-
160- if "cloudflare" not in html_content :
166+ try :
167+ html_content = await page .content ()
168+ except Exception :
169+ html_content = ""
170+
171+ if "cloudflare" not in html_content .lower ():
161172 self .log_message ("No Cloudflare protection detected on the page -- either not protected or already bypassed" )
162173 return True
163-
174+
164175 # Check if we need to solve a challenge
165176 if await self .is_bypassed (page ):
166177 self .log_message ("No Cloudflare challenge detected or already bypassed" )
167178 return True
168179
169- self .log_message ("Cloudflare challenge detected. Attempting to solve..." )
180+ self .log_message ("Cloudflare challenge detected. Waiting for auto-resolve..." )
181+
182+ # Most Cloudflare interstitial challenges now auto-resolve without
183+ # user interaction. Poll for the challenge to clear before trying
184+ # the click solver.
185+ for i in range (self .max_retries ):
186+ await asyncio .sleep (5 )
187+ try :
188+ if await self .is_bypassed (page ):
189+ self .log_message ("Cloudflare challenge auto-resolved" )
190+ await asyncio .sleep (1 )
191+ return True
192+ except Exception :
193+ # Page may be navigating — that's a good sign
194+ pass
195+
196+ # If auto-resolve failed, try the click solver as a fallback
197+ # (for turnstile or interactive challenges)
198+ self .log_message ("Auto-resolve timed out. Attempting click solver..." )
170199 challenge_type = await self .determine_challenge_type (page )
171200 if not challenge_type :
172201 self .log_message ("Could not determine challenge type" )
173202 return False
174-
203+
175204 expected_selector = "#root"
176205 captcha_container = page
177- is_solved = False
178- async with ClickSolver (framework = FrameworkType .CAMOUFOX , page = page , max_attempts = 2 , attempt_delay = 1 ) as solver :
179-
206+ is_solved = False
207+ async with ClickSolver (framework = FrameworkType .CAMOUFOX , page = page , max_attempts = 3 , attempt_delay = 2 ) as solver :
180208 await solver .solve_captcha (
181209 captcha_container = captcha_container ,
182210 captcha_type = challenge_type ,
183211 expected_content_selector = expected_selector ,)
184-
185212 is_solved = "just a moment" not in await page .title ()
186-
187213
188214 if is_solved :
189- self .log_message ("✅ Cloudflare challenge solved successfully!" )
190- # Wait a bit more to ensure cookies are set
215+ self .log_message ("Cloudflare challenge solved successfully!" )
191216 await asyncio .sleep (1 )
192217 return True
193218 else :
194- self .log_message ("❌ Failed to solve Cloudflare challenge" )
219+ self .log_message ("Failed to solve Cloudflare challenge" )
195220 return False
196221
197222 except Exception as e :
0 commit comments