@@ -202,6 +202,99 @@ class CloudflareChallengeService {
202202 await page . waitForTimeout ( 900 ) ;
203203 return this . detect ( page ) ;
204204 }
205+
206+ public async solveChallenge ( page : Page ) : Promise < boolean > {
207+ console . log ( 'CF: Attempting to solve Cloudflare Challenge...' ) ;
208+ try {
209+ console . log ( 'CF: Waiting 5 seconds for initial load...' ) ;
210+ await page . waitForTimeout ( 5000 ) ;
211+
212+ const title = await page . title ( ) . catch ( ( ) => '' ) ;
213+ const isCfInterstitial = title . toLowerCase ( ) . includes ( 'just a moment' ) ;
214+
215+ if ( ! isCfInterstitial ) {
216+ const cfInput = page . locator ( '[name="cf-turnstile-response"]' ) . first ( ) ;
217+ try {
218+ await cfInput . waitFor ( { state : 'attached' , timeout : 5000 } ) ;
219+ } catch {
220+ console . log ( 'CF: No Cloudflare Challenge input detected. Proceeding.' ) ;
221+ return false ;
222+ }
223+ }
224+
225+ console . log ( 'CF: Cloudflare challenge page detected! Waiting 3s...' ) ;
226+ await page . waitForTimeout ( 3000 ) ;
227+
228+ const iframeElement = page . locator ( "iframe[src*='challenges.cloudflare.com']" ) . first ( ) ;
229+ let box : { x : number ; y : number ; width : number ; height : number } | null = null ;
230+
231+ try {
232+ await iframeElement . waitFor ( { state : 'attached' , timeout : 8000 } ) ;
233+ box = await iframeElement . boundingBox ( ) ;
234+ if ( box ) {
235+ console . log ( `CF: Turnstile IFRAME bounds: x=${ Math . round ( box . x ) } y=${ Math . round ( box . y ) } w=${ Math . round ( box . width ) } h=${ Math . round ( box . height ) } ` ) ;
236+ }
237+ } catch ( e ) {
238+ console . log ( `CF: Could not get iframe bounding box: ${ ( e as Error ) . message } ` ) ;
239+ }
240+
241+ if ( ! box || box . width === 0 || box . height === 0 ) {
242+ console . log ( "CF: Iframe element has no visible bounds. Trying frame URL matching..." ) ;
243+ for ( const frame of page . frames ( ) ) {
244+ if ( frame . url ( ) . includes ( "challenges.cloudflare.com" ) ) {
245+ console . log ( `CF: Found CF frame: ${ frame . url ( ) . substring ( 0 , 100 ) } ` ) ;
246+ try {
247+ const frameElement = await frame . frameElement ( ) ;
248+ box = await frameElement . boundingBox ( ) ;
249+ if ( box ) {
250+ console . log ( `CF: Frame element bounds: x=${ Math . round ( box . x ) } y=${ Math . round ( box . y ) } w=${ Math . round ( box . width ) } h=${ Math . round ( box . height ) } ` ) ;
251+ break ;
252+ }
253+ } catch ( e2 ) {
254+ console . log ( `CF: frameElement() fallback failed: ${ ( e2 as Error ) . message } ` ) ;
255+ }
256+ }
257+ }
258+ }
259+
260+ if ( box && box . width > 0 && box . height > 0 ) {
261+ const xTarget = box . x + 30 + ( Math . random ( ) * 10 - 5 ) ;
262+ const yTarget = box . y + ( box . height / 2 ) + ( Math . random ( ) * 6 - 3 ) ;
263+
264+ console . log ( `CF: Clicking checkbox at (${ xTarget . toFixed ( 1 ) } , ${ yTarget . toFixed ( 1 ) } )...` ) ;
265+
266+ const steps = Math . floor ( Math . random ( ) * 9 ) + 10 ; // 10 to 18 steps
267+ await page . mouse . move ( xTarget , yTarget , { steps } ) ;
268+
269+ const hoverDelay = Math . random ( ) * 250 + 150 ; // 150 to 400ms
270+ await page . waitForTimeout ( hoverDelay ) ;
271+
272+ await page . mouse . down ( ) ;
273+ const clickDuration = Math . floor ( Math . random ( ) * 81 ) + 50 ; // 50 to 130ms
274+ await page . waitForTimeout ( clickDuration ) ;
275+ await page . mouse . up ( ) ;
276+
277+ console . log ( "CF: Click executed. Waiting 8s for verification..." ) ;
278+ await page . waitForTimeout ( 8000 ) ;
279+
280+ const newTitle = await page . title ( ) . catch ( ( ) => '' ) ;
281+ if ( ! newTitle . toLowerCase ( ) . includes ( "just a moment" ) ) {
282+ console . log ( "CF: Challenge appears solved! Page title changed." ) ;
283+ return true ;
284+ } else {
285+ console . log ( "CF: Page title still shows challenge. May need retry." ) ;
286+ }
287+ } else {
288+ console . log ( "CF: FAILED - Could not find any clickable Turnstile iframe." ) ;
289+ }
290+
291+ } catch ( e ) {
292+ console . error ( `CF: Exception during bypass:` , e ) ;
293+ }
294+
295+ console . log ( "CF: Proceeding without successful bypass or bypass failed." ) ;
296+ return false ;
297+ }
205298}
206299
207300export const cloudflareChallengeService = CloudflareChallengeService . getInstance ( ) ;
0 commit comments