@@ -27,6 +27,7 @@ type LoginState =
2727 | 'LOGIN_PASSWORDLESS'
2828 | 'GET_A_CODE'
2929 | 'GET_A_CODE_2'
30+ | 'OTP_CODE_ENTRY'
3031 | 'UNKNOWN'
3132 | 'CHROMEWEBDATA_ERROR'
3233
@@ -56,9 +57,13 @@ export class Login {
5657 totpInputOld : 'form[name="OneTimeCodeViewForm"]' ,
5758 identityBanner : '[data-testid="identityBanner"]' ,
5859 viewFooter : '[data-testid="viewFooter"] >> [role="button"]' ,
60+ otherWaysToSignIn : '[data-testid="viewFooter"] span[role="button"]' ,
61+ otpCodeEntry : '[data-testid="codeEntry"]' ,
62+ backButton : '#back-button' ,
5963 bingProfile : '#id_n' ,
6064 requestToken : 'input[name="__RequestVerificationToken"]' ,
61- requestTokenMeta : 'meta[name="__RequestVerificationToken"]'
65+ requestTokenMeta : 'meta[name="__RequestVerificationToken"]' ,
66+ otpInput : 'div[data-testid="codeEntry"]'
6267 } as const
6368
6469 constructor ( private bot : MicrosoftRewardsBot ) {
@@ -73,7 +78,7 @@ export class Login {
7378 try {
7479 this . bot . logger . info ( this . bot . isMobile , 'LOGIN' , 'Starting login process' )
7580
76- await page . goto ( 'https://www.bing.com/rewards/dashboard' , { waitUntil : 'domcontentloaded' } ) . catch ( ( ) => { } )
81+ await page . goto ( 'https://www.bing.com/rewards/dashboard' , { waitUntil : 'domcontentloaded' } ) . catch ( ( ) => { } )
7782 await this . bot . utils . wait ( 2000 )
7883 await this . bot . browser . utils . reloadBadPage ( page )
7984 await this . bot . browser . utils . disableFido ( page )
@@ -149,7 +154,7 @@ export class Login {
149154 }
150155
151156 private async detectCurrentState ( page : Page , account ?: Account ) : Promise < LoginState > {
152- await page . waitForLoadState ( 'networkidle' , { timeout : 5000 } ) . catch ( ( ) => { } )
157+ await page . waitForLoadState ( 'networkidle' , { timeout : 5000 } ) . catch ( ( ) => { } )
153158
154159 const url = new URL ( page . url ( ) )
155160 this . bot . logger . debug ( this . bot . isMobile , 'DETECT-STATE' , `Current URL: ${ url . hostname } ${ url . pathname } ` )
@@ -183,7 +188,9 @@ export class Login {
183188 [ this . selectors . emailIconOld , 'SIGN_IN_ANOTHER_WAY_EMAIL' ] ,
184189 [ this . selectors . passwordlessCheck , 'LOGIN_PASSWORDLESS' ] ,
185190 [ this . selectors . totpInput , '2FA_TOTP' ] ,
186- [ this . selectors . totpInputOld , '2FA_TOTP' ]
191+ [ this . selectors . totpInputOld , '2FA_TOTP' ] ,
192+ [ this . selectors . otpCodeEntry , 'OTP_CODE_ENTRY' ] , // PR 450
193+ [ this . selectors . otpInput , 'OTP_CODE_ENTRY' ] // My Fix
187194 ]
188195
189196 const results = await Promise . all (
@@ -243,8 +250,11 @@ export class Login {
243250 'KMSI_PROMPT' ,
244251 'PASSWORD_INPUT' ,
245252 'EMAIL_INPUT' ,
253+ 'SIGN_IN_ANOTHER_WAY' , // Prefer password option over email code
246254 'SIGN_IN_ANOTHER_WAY_EMAIL' ,
247- 'SIGN_IN_ANOTHER_WAY' ,
255+ 'OTP_CODE_ENTRY' ,
256+ 'GET_A_CODE' ,
257+ 'GET_A_CODE_2' ,
248258 'LOGIN_PASSWORDLESS' ,
249259 '2FA_TOTP'
250260 ]
@@ -308,12 +318,56 @@ export class Login {
308318 }
309319
310320 case 'GET_A_CODE' : {
311- this . bot . logger . info ( this . bot . isMobile , 'LOGIN' , 'Attempting to bypass "Get code" via footer' )
312- await this . bot . browser . utils . ghostClick ( page , this . selectors . viewFooter )
313- await page . waitForLoadState ( 'networkidle' , { timeout : 5000 } ) . catch ( ( ) => {
314- this . bot . logger . debug ( this . bot . isMobile , 'LOGIN' , 'Network idle timeout after footer click' )
315- } )
316- this . bot . logger . info ( this . bot . isMobile , 'LOGIN' , 'Footer clicked, proceeding' )
321+ this . bot . logger . info ( this . bot . isMobile , 'LOGIN' , 'Attempting to bypass "Get code" page' )
322+
323+ // Try to find "Other ways to sign in" link
324+ const otherWaysLink = await page
325+ . waitForSelector ( this . selectors . otherWaysToSignIn , { state : 'visible' , timeout : 3000 } )
326+ . catch ( ( ) => null )
327+
328+ if ( otherWaysLink ) {
329+ this . bot . logger . info ( this . bot . isMobile , 'LOGIN' , 'Found "Other ways to sign in" link' )
330+ await this . bot . browser . utils . ghostClick ( page , this . selectors . otherWaysToSignIn )
331+ await page . waitForLoadState ( 'networkidle' , { timeout : 5000 } ) . catch ( ( ) => {
332+ this . bot . logger . debug (
333+ this . bot . isMobile ,
334+ 'LOGIN' ,
335+ 'Network idle timeout after clicking other ways'
336+ )
337+ } )
338+ this . bot . logger . info ( this . bot . isMobile , 'LOGIN' , '"Other ways to sign in" clicked' )
339+ return true
340+ }
341+
342+ // Fallback: try the generic viewFooter selector
343+ const footerLink = await page
344+ . waitForSelector ( this . selectors . viewFooter , { state : 'visible' , timeout : 2000 } )
345+ . catch ( ( ) => null )
346+
347+ if ( footerLink ) {
348+ await this . bot . browser . utils . ghostClick ( page , this . selectors . viewFooter )
349+ await page . waitForLoadState ( 'networkidle' , { timeout : 5000 } ) . catch ( ( ) => {
350+ this . bot . logger . debug ( this . bot . isMobile , 'LOGIN' , 'Network idle timeout after footer click' )
351+ } )
352+ this . bot . logger . info ( this . bot . isMobile , 'LOGIN' , 'Footer link clicked' )
353+ return true
354+ }
355+
356+ // If no links found, try clicking back button
357+ const backBtn = await page
358+ . waitForSelector ( this . selectors . backButton , { state : 'visible' , timeout : 2000 } )
359+ . catch ( ( ) => null )
360+
361+ if ( backBtn ) {
362+ this . bot . logger . info ( this . bot . isMobile , 'LOGIN' , 'No sign in options found, clicking back button' )
363+ await this . bot . browser . utils . ghostClick ( page , this . selectors . backButton )
364+ await page . waitForLoadState ( 'networkidle' , { timeout : 5000 } ) . catch ( ( ) => {
365+ this . bot . logger . debug ( this . bot . isMobile , 'LOGIN' , 'Network idle timeout after back button' )
366+ } )
367+ return true
368+ }
369+
370+ this . bot . logger . warn ( this . bot . isMobile , 'LOGIN' , 'Could not find way to bypass Get Code page' )
317371 return true
318372 }
319373
@@ -381,7 +435,7 @@ export class Login {
381435 waitUntil : 'domcontentloaded' ,
382436 timeout : 10000
383437 } )
384- . catch ( ( ) => { } )
438+ . catch ( ( ) => { } )
385439 await this . bot . utils . wait ( 3000 )
386440 this . bot . logger . info ( this . bot . isMobile , 'LOGIN' , 'Recovery navigation successful' )
387441 return true
@@ -392,7 +446,7 @@ export class Login {
392446 waitUntil : 'domcontentloaded' ,
393447 timeout : 10000
394448 } )
395- . catch ( ( ) => { } )
449+ . catch ( ( ) => { } )
396450 await this . bot . utils . wait ( 3000 )
397451 this . bot . logger . info ( this . bot . isMobile , 'LOGIN' , 'Fallback navigation successful' )
398452 return true
@@ -447,6 +501,38 @@ export class Login {
447501 return true
448502 }
449503
504+ case 'OTP_CODE_ENTRY' : {
505+ this . bot . logger . info ( this . bot . isMobile , 'LOGIN' , 'OTP code entry page detected, attempting to find password option' )
506+
507+ // My Fix: Click "Use your password" footer
508+ const footerLink = await page
509+ . waitForSelector ( this . selectors . viewFooter , { state : 'visible' , timeout : 2000 } )
510+ . catch ( ( ) => null )
511+
512+ if ( footerLink ) {
513+ await this . bot . browser . utils . ghostClick ( page , this . selectors . viewFooter )
514+ this . bot . logger . info ( this . bot . isMobile , 'LOGIN' , 'Footer link clicked' )
515+ } else {
516+ // PR 450 Fix: Click Back Button if footer not found
517+ const backButton = await page
518+ . waitForSelector ( this . selectors . backButton , { state : 'visible' , timeout : 2000 } )
519+ . catch ( ( ) => null )
520+
521+ if ( backButton ) {
522+ await this . bot . browser . utils . ghostClick ( page , this . selectors . backButton )
523+ this . bot . logger . info ( this . bot . isMobile , 'LOGIN' , 'Back button clicked' )
524+ } else {
525+ this . bot . logger . warn ( this . bot . isMobile , 'LOGIN' , 'No navigation option found on OTP page' )
526+ }
527+ }
528+
529+ await page . waitForLoadState ( 'networkidle' , { timeout : 5000 } ) . catch ( ( ) => {
530+ this . bot . logger . debug ( this . bot . isMobile , 'LOGIN' , 'Network idle timeout after OTP navigation' )
531+ } )
532+ this . bot . logger . info ( this . bot . isMobile , 'LOGIN' , 'Navigated back from OTP entry page' )
533+ return true
534+ }
535+
450536 case 'UNKNOWN' : {
451537 const url = new URL ( page . url ( ) )
452538 this . bot . logger . warn (
@@ -466,7 +552,7 @@ export class Login {
466552 private async finalizeLogin ( page : Page , email : string ) {
467553 this . bot . logger . info ( this . bot . isMobile , 'LOGIN' , 'Finalizing login' )
468554
469- await page . goto ( this . bot . config . baseURL , { waitUntil : 'networkidle' , timeout : 10000 } ) . catch ( ( ) => { } )
555+ await page . goto ( this . bot . config . baseURL , { waitUntil : 'networkidle' , timeout : 10000 } ) . catch ( ( ) => { } )
470556
471557 const loginRewardsSuccess = new URL ( page . url ( ) ) . hostname === 'rewards.bing.com'
472558 if ( loginRewardsSuccess ) {
@@ -497,7 +583,7 @@ export class Login {
497583 this . bot . logger . info ( this . bot . isMobile , 'LOGIN-BING' , 'Verifying Bing session' )
498584
499585 try {
500- await page . goto ( url , { waitUntil : 'networkidle' , timeout : 10000 } ) . catch ( ( ) => { } )
586+ await page . goto ( url , { waitUntil : 'networkidle' , timeout : 10000 } ) . catch ( ( ) => { } )
501587
502588 for ( let i = 0 ; i < loopMax ; i ++ ) {
503589 if ( page . isClosed ( ) ) break
@@ -519,7 +605,7 @@ export class Login {
519605 )
520606
521607 if ( atBingHome ) {
522- await this . bot . browser . utils . tryDismissAllMessages ( page ) . catch ( ( ) => { } )
608+ await this . bot . browser . utils . tryDismissAllMessages ( page ) . catch ( ( ) => { } )
523609
524610 const signedIn = await page
525611 . waitForSelector ( this . selectors . bingProfile , { timeout : 3000 } )
@@ -555,7 +641,7 @@ export class Login {
555641 try {
556642 await page
557643 . goto ( `${ this . bot . config . baseURL } ?_=${ Date . now ( ) } ` , { waitUntil : 'networkidle' , timeout : 10000 } )
558- . catch ( ( ) => { } )
644+ . catch ( ( ) => { } )
559645
560646 for ( let i = 0 ; i < loopMax ; i ++ ) {
561647 if ( page . isClosed ( ) ) break
0 commit comments