diff --git a/pkg/messagix/bloks/cmd/main.go b/pkg/messagix/bloks/cmd/main.go index 2c11c4c5..a70ecc03 100644 --- a/pkg/messagix/bloks/cmd/main.go +++ b/pkg/messagix/bloks/cmd/main.go @@ -205,6 +205,10 @@ func mainE() error { } return nil }, + CancelTimer: func(name string) error { + fmt.Printf("timer cancelled: %s\n", name) + return nil + }, OpenURL: func(url string) error { if lastURL != "" { return fmt.Errorf("already opened a url this session") diff --git a/pkg/messagix/bloks/interp.go b/pkg/messagix/bloks/interp.go index d748c8e5..44345b2f 100644 --- a/pkg/messagix/bloks/interp.go +++ b/pkg/messagix/bloks/interp.go @@ -37,6 +37,7 @@ type InterpBridge struct { DisplayNewScreen func(context.Context, string, *BloksBundle) error HandleLoginResponse func(ctx context.Context, data string) error StartTimer func(name string, interval time.Duration, callback func() error) error + CancelTimer func(name string) error OpenURL func(url string) error HandleVariableChange func(ctx context.Context, name string, value *BloksScriptLiteral) error } @@ -171,6 +172,11 @@ func NewInterpreter(ctx context.Context, b *BloksBundle, br *InterpBridge, old * return fmt.Errorf("unhandled timer %s", name) } } + if br.CancelTimer == nil { + br.CancelTimer = func(name string) error { + return fmt.Errorf("unhandled timer cancel %s", name) + } + } if br.OpenURL == nil { br.OpenURL = func(url string) error { return fmt.Errorf("unhandled url %s", url) @@ -1193,6 +1199,17 @@ func (i *Interpreter) Evaluate(ctx context.Context, form *BloksScriptNode) (*Blo return nil, err } return BloksNothing, nil + case "bk.action.timer.Cancel": + // Args are (timer context, name), the first of which we have no use for. + name, err := evalAs[string](ctx, i, &call.Args[1], "timer.cancel") + if err != nil { + return nil, err + } + err = i.Bridge.CancelTimer(name) + if err != nil { + return nil, err + } + return BloksNothing, nil case "bk.action.caa.PresentCheckpointsFlow": flowB, err := evalAs[string](ctx, i, &call.Args[0], "presentcheckpointsflow") if err != nil { diff --git a/pkg/messagix/bloks/selenium.go b/pkg/messagix/bloks/selenium.go index 2e03eee4..1be14161 100644 --- a/pkg/messagix/bloks/selenium.go +++ b/pkg/messagix/bloks/selenium.go @@ -32,6 +32,7 @@ import ( var ( ErrLoginPhoneNumber = bridgev2.RespError{ErrCode: "FI.MAU.META_PHONE_NUMBER", Err: "Phone number login is not supported, please try email address or username", StatusCode: http.StatusBadRequest} ErrLoginInvalidUsername = bridgev2.RespError{ErrCode: "FI.MAU.META_MATRIX_ID", Err: "That doesn't look like a valid username, please enter your Facebook email address or username", StatusCode: http.StatusBadRequest} + ErrLoginAFADStopped = bridgev2.RespError{ErrCode: "FI.MAU.META_AFAD_STOPPED", Err: "The approval request expired or was denied, please try logging in again", StatusCode: http.StatusBadRequest} ErrLoginMandatoryOAuth = bridgev2.RespError{ErrCode: "FI.MAU.META_OAUTH_MANDATORY", Err: "Meta is requiring Google sign-in which is not supported. Please try adding a different MFA method to your Facebook account from another device", StatusCode: http.StatusBadRequest} ) @@ -604,6 +605,8 @@ func NewBrowser(cfg *BrowserConfig) (*Browser, error) { newState = StateBackupCodePage case "com.bloks.www.ap.two_step_verification.contactpoint_chooser": newState = StateChooseNumberPage + case "com.bloks.www.approve_from_another_device.xmds.challenged_device_denied": + return ErrLoginAFADStopped case "com.bloks.www.two_step_verification.enter_whatsapp_code": newState = StateWhatsAppPage case "com.bloks.www.ap.passkey_auth": @@ -643,6 +646,16 @@ func NewBrowser(cfg *BrowserConfig) (*Browser, error) { } return nil }, + CancelTimer: func(name string) error { + switch name { + case "approve_from_another_device_polling_timer": + b.AFADInterval = 0 + b.AFADCallback = nil + default: + return fmt.Errorf("unexpected timer cancel %s", name) + } + return nil + }, OpenURL: func(url string) error { b.DisplayedURL = url return nil @@ -1377,9 +1390,15 @@ func (b *Browser) DoLoginStep(ctx context.Context, userInput map[string]string) case StateAFADPageWaiting: for b.State == StateAFADPageWaiting { + if b.AFADCallback == nil { + return nil, ErrLoginAFADStopped + } time.Sleep(b.AFADInterval) err := b.AFADCallback() if err != nil { + if ctxErr := ctx.Err(); ctxErr != nil { + return nil, fmt.Errorf("login cancelled while waiting for approval: %w", ctxErr) + } return nil, fmt.Errorf("AFAD callback: %w", err) } }