Skip to content

Commit

Permalink
Handle FB consent required state
Browse files Browse the repository at this point in the history
  • Loading branch information
tulir committed Mar 6, 2024
1 parent ebf4b05 commit 05d10f0
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 29 deletions.
29 changes: 2 additions & 27 deletions messagix/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,6 @@ const SecCHMobile = "?0"
const SecCHModel = ""
const SecCHPrefersColorScheme = "light"

var (
ErrTokenInvalidated = errors.New("access token is no longer valid")
ErrChallengeRequired = errors.New("challenge required")
ErrConsentRequired = errors.New("consent required")
ErrRequestFailed = errors.New("failed to send request")
ErrResponseReadFailed = errors.New("failed to read response body")
ErrMaxRetriesReached = errors.New("maximum retries reached")
)

type EventHandler func(evt interface{})
type Client struct {
Instagram *InstagramMethods
Expand Down Expand Up @@ -95,24 +86,8 @@ func NewClient(cookies *cookies.Cookies, logger zerolog.Logger) *Client {
ResponseHeaderTimeout: 40 * time.Second,
ForceAttemptHTTP2: true,
},
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if req.Response == nil {
return nil
}
if req.URL.Path == "/challenge/" {
return fmt.Errorf("%w: redirected to %s", ErrChallengeRequired, req.URL.String())
} else if req.URL.Path == "/consent/" {
return fmt.Errorf("%w: redirected to %s", ErrConsentRequired, req.URL.String())
}
respCookies := req.Response.Cookies()
for _, cookie := range respCookies {
if (cookie.Name == "xs" || cookie.Name == "sessionid") && cookie.MaxAge < 0 {
return fmt.Errorf("%w: %s cookie was deleted", ErrTokenInvalidated, cookie.Name)
}
}
return nil
},
Timeout: 60 * time.Second,
CheckRedirect: checkHTTPRedirect,
Timeout: 60 * time.Second,
},
cookies: cookies,
Logger: logger,
Expand Down
35 changes: 34 additions & 1 deletion messagix/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,39 @@ func (c *Client) NewHttpQuery() *HttpQuery {

const MaxHTTPRetries = 5

var (
ErrTokenInvalidated = errors.New("access token is no longer valid")
ErrChallengeRequired = errors.New("challenge required")
ErrConsentRequired = errors.New("consent required")
ErrRequestFailed = errors.New("failed to send request")
ErrResponseReadFailed = errors.New("failed to read response body")
ErrMaxRetriesReached = errors.New("maximum retries reached")
)

func isPermanentRequestError(err error) bool {
return errors.Is(err, ErrTokenInvalidated) ||
errors.Is(err, ErrChallengeRequired) ||
errors.Is(err, ErrConsentRequired)
}

func checkHTTPRedirect(req *http.Request, _ []*http.Request) error {
if req.Response == nil {
return nil
}
if req.URL.Path == "/challenge/" {
return fmt.Errorf("%w: redirected to %s", ErrChallengeRequired, req.URL.String())
} else if req.URL.Path == "/consent/" || req.URL.Path == "/privacy/consent/" {
return fmt.Errorf("%w: redirected to %s", ErrConsentRequired, req.URL.String())
}
respCookies := req.Response.Cookies()
for _, cookie := range respCookies {
if (cookie.Name == "xs" || cookie.Name == "sessionid") && cookie.MaxAge < 0 {
return fmt.Errorf("%w: %s cookie was deleted", ErrTokenInvalidated, cookie.Name)
}
}
return nil
}

func (c *Client) MakeRequest(url string, method string, headers http.Header, payload []byte, contentType types.ContentType) (*http.Response, []byte, error) {
var attempts int
for {
Expand All @@ -100,7 +133,7 @@ func (c *Client) MakeRequest(url string, method string, headers http.Header, pay
Dur("duration", dur).
Msg("Request failed, giving up")
return nil, nil, fmt.Errorf("%w: %w", ErrMaxRetriesReached, err)
} else if errors.Is(err, ErrConsentRequired) || errors.Is(err, ErrChallengeRequired) || errors.Is(err, ErrTokenInvalidated) {
} else if isPermanentRequestError(err) {
c.Logger.Err(err).
Str("url", url).
Str("method", method).
Expand Down
8 changes: 7 additions & 1 deletion user.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ const (
IGChallengeRequiredMaybe status.BridgeStateErrorCode = "ig-challenge-required-maybe"
MetaServerUnavailable status.BridgeStateErrorCode = "meta-server-unavailable"
IGConsentRequired status.BridgeStateErrorCode = "ig-consent-required"
FBConsentRequired status.BridgeStateErrorCode = "fb-consent-required"
)

func init() {
Expand All @@ -193,6 +194,7 @@ func init() {
IGChallengeRequired: "Challenge required, please check the Instagram website to continue",
IGChallengeRequiredMaybe: "Connection refused, please check the Instagram website to continue",
IGConsentRequired: "Consent required, please check the Instagram website to continue",
FBConsentRequired: "Consent required, please check the Facebook website to continue",
MetaServerUnavailable: "Connection refused by server",
MetaConnectError: "Unknown connection error",
})
Expand Down Expand Up @@ -506,9 +508,13 @@ func (user *User) unlockedConnect() {
Error: IGChallengeRequired,
})
} else if errors.Is(err, messagix.ErrConsentRequired) {
code := IGConsentRequired
if user.bridge.Config.Meta.Mode.IsMessenger() {
code = FBConsentRequired
}
user.BridgeState.Send(status.BridgeState{
StateEvent: status.StateBadCredentials,
Error: IGConsentRequired,
Error: code,
})
} else if lsErr := (&types.ErrorResponse{}); errors.As(err, &lsErr) {
stateEvt := status.StateUnknownError
Expand Down

0 comments on commit 05d10f0

Please sign in to comment.