Esempio n. 1
0
    def _check_response_for_errors(self, resp: requests.Response) -> None:
        try:
            parsed = resp.json()
        except json.JSONDecodeError:
            if not resp.ok:
                if resp.status_code == 404 and '/friendships/' in resp.url:
                    raise InvalidUserId(
                        f"url: {resp.url} is not recognized by Instagram")

                logger.exception(
                    f"response received: \n{resp.text}\nurl: {resp.url}\nstatus code: {resp.status_code}"
                )
                raise BadResponse("Received a non-200 response from Instagram")
            return
        if resp.ok:
            return

        if parsed.get('description') == 'invalid password':
            raise IncorrectLoginDetails(
                "Instagram does not recognize the provided login details")
        if parsed.get('message') in ("checkpoint_required",
                                     "challenge_required"):
            if not hasattr(self, '_handle_challenge'):
                raise BadResponse(
                    "Challenge required. ChallengeMixin is not mixed in.")
            eh = self._handle_challenge(resp)
            if eh:
                return
        if parsed.get('message') == 'feedback_required':
            # TODO: implement a handler for this error
            raise BadResponse(
                "Something unexpected happened. Please check the IG app.")
        if parsed.get('message') == 'rate_limit_error':
            raise TimeoutError("Calm down. Please try again in a few minutes.")
        raise BadResponse("Received a non-200 response from Instagram")
Esempio n. 2
0
    def _check_response_for_errors(self, resp: requests.Response) -> None:
        if resp.ok:
            return

        try:
            parsed = resp.json()
        except json.JSONDecodeError:
            if resp.status_code == 404 and '/friendships/' in resp.url:
                raise InvalidUserId(f"account id: {resp.url.split('/')[-2]} is not recognized by Instagram or you do not have a relation with this account.")

            logger.exception(f"response received: \n{resp.text}\nurl: {resp.url}\nstatus code: {resp.status_code}")
            raise BadResponse("Received a non-200 response from Instagram")

        if parsed.get('error_type') == 'bad_password':
            raise IncorrectLoginDetails("Instagram does not recognize the provided login details")
        if parsed.get('message') in ("checkpoint_required", "challenge_required"):
            if not hasattr(self, '_handle_challenge'):
                raise BadResponse("Challenge required. ChallengeMixin is not mixed in.")
            eh = self._handle_challenge(resp)
            if eh:
                return
        if parsed.get('message') == 'feedback_required':
            if os.environ.get("ENABLE_INSTAUTO_USAGE_METRICS", True):
                # This logs which actions cause limitations on Instagram accounts.
                # I use this data to focus my development on area's where it's most needed.
                requests.post('https://instauto.rooy.dev/feedback_required', data={
                    'feedback_url': parsed.get('feedback_url'),
                    'category': parsed.get('category')
                })
            raise BadResponse("Something unexpected happened. Please check the IG app.")
        if parsed.get('message') == 'rate_limit_error':
            raise TimeoutError("Calm down. Please try again in a few minutes.")
        if parsed.get('message') == 'Not authorized to view user':
            raise AuthorizationError("This is a private user, which you do not follow.")
        raise BadResponse("Received a non-200 response from Instagram")
Esempio n. 3
0
    def _handle_challenge(self, resp: requests.Response) -> bool:
        resp_data = self._json_loads(resp.text)
        # pyre-ignore[6]
        if resp_data['message'] not in ('challenge_required',
                                        'checkpoint_required'):
            raise BadResponse("Challenge required, but no URL provided.")
        # pyre-ignore[6]
        api_path = resp_data['challenge']['api_path'][1:]
        resp = self._request(endpoint=api_path,
                             method=Method.GET,
                             query={
                                 "guid": self.state.uuid,
                                 "device_id": self.state.android_id
                             })
        data = self._json_loads(resp.text)

        base_body = {
            "_csrftoken": self._session.cookies['csrftoken'],
            "_uuid": self.state.uuid,
            "bloks_versioning_id": self.state.bloks_version_id,
            "post": 1,
        }
        body = base_body.copy()
        body["choice"] = 1
        #        body["choice"] = int(data.get("step_data", {}).get("choice", 0))

        _ = self._request(endpoint=api_path, method=Method.POST, body=body)

        security_code = input(
            "Verification needed. Type verification code here: ")
        body = base_body.copy()
        body["security_code"] = security_code
        _ = self._request(endpoint=api_path, method=Method.POST, body=body)
        return True
Esempio n. 4
0
    def _handle_challenge(self, resp: requests.Response) -> bool:
        resp_data = resp.json()
        if resp_data['message'] not in ('challenge_required',
                                        'checkpoint_required'):
            raise BadResponse("Challenge required, but no URL provided.")

        api_path = resp_data['challenge']['api_path'][1:]
        resp_data2 = self._request(endpoint=api_path,
                                   method=Method.GET,
                                   query={
                                       "guid": self.state.uuid,
                                       "device_id": self.state.android_id
                                   })
        # TODO: Add support for different kinds of challenges.
        #       Currently, only the verification pin challenge is supported, and other challenges, such as the
        #       'verify this was you', do not work.
        resp_json2 = resp_data2.json()
        if int(resp_json2.get("step_data", {}).get("choice", 0)) == 1:
            # server only support choice 1, this is requiry some other devices to confirm send verification, like web
            _ = self._request(
                endpoint=api_path,
                method=Method.POST,
                data={
                    "choice":
                    1,  # TODO: enum to confirm send verification code.
                    "_csrftoken": self._session.cookies['csrftoken'],
                    "_uuid": self.state.uuid,
                    "bloks_versioning_id": self.state.bloks_version_id,
                    "post": 1
                })
            logger.warning(
                "You may should confirm in some other logged device this was me to obtain the verification code."
            )
        else:
            _ = self._request(
                endpoint=api_path,
                method=Method.POST,
                data={
                    "choice":
                    0,  # TODO: enum phone/email verification. Which value represent which one?
                    "_csrftoken": self._session.cookies['csrftoken'],
                    "_uuid": self.state.uuid,
                    "bloks_versioning_id": self.state.bloks_version_id,
                    "post": 1
                })

        security_code = input(
            "Verification needed. Type verification code here: ")
        _ = self._request(endpoint=api_path,
                          method=Method.POST,
                          data={
                              "_csrftoken": self._session.cookies['csrftoken'],
                              "_uuid": self.state.uuid,
                              "bloks_versioning_id":
                              self.state.bloks_version_id,
                              "post": 1,
                              "security_code": security_code
                          })
        return True
Esempio n. 5
0
 def _handle_feedback_required(self, parsed: dict) -> None:
     if os.environ.get("ENABLE_INSTAUTO_USAGE_METRICS", True):
         # This logs which actions cause limitations on Instagram accounts.
         # I use this data to focus my development on area's where it's most needed.
         requests.post('https://instauto.rooy.dev/feedback_required',
                       data={
                           'feedback_url': parsed.get('feedback_url'),
                           'category': parsed.get('category')
                       })
     raise BadResponse(
         "Something unexpected happened. Please check the IG app.")
Esempio n. 6
0
    def _check_response_for_errors(self, resp: requests.Response) -> None:
        if resp.ok:
            return

        try:
            # pyre-ignore[9]: assume the response is a dictionary
            parsed: Dict[Any, Any] = self._json_loads(resp.text)
        except orjson.JSONDecodeError:
            if resp.status_code == 404 and '/friendships/' in resp.url:
                raise InvalidUserId(
                    f"account id: {resp.url.split('/')[-2]} is not recognized "
                    f"by Instagram or you do not have a relation with this account."
                )

            logger.exception(
                f"response received: \n{resp.text}\nurl: {resp.url}\nstatus code: {resp.status_code}"
            )
            raise BadResponse("Received a non-200 response from Instagram")

        message = parsed.get('message')
        error_type = parsed.get('error_type')
        two_factor_required = parsed.get('two_factor_required', False)

        if two_factor_required:
            return self._handle_2fa(parsed)
        if error_type == 'bad_password':
            raise IncorrectLoginDetails(
                "Instagram does not recognize the provided login details")
        if message in ("checkpoint_required", "challenge_required"):
            if self._handle_checkpoint(resp):
                return
        if error_type == 'feedback_required':
            self._handle_feedback_required(parsed)
        if error_type == 'rate_limit_error':
            raise Exception("Calm down. Please try again in a few minutes.")
        if error_type == 'Not authorized to view user':
            raise AuthorizationError(
                "This is a private user, which you do not follow.")
        raise BadResponse(f"Received a non-200 response from Instagram: \
            {message}")
Esempio n. 7
0
    def _check_response_for_errors(self, resp: requests.Response) -> None:
        if resp.ok:
            return

        try:
            parsed = resp.json()
        except json.JSONDecodeError:
            if resp.status_code == 404 and '/friendships/' in resp.url:
                raise InvalidUserId(
                    f"account id: {resp.url.split('/')[-2]} is not recognized by Instagram or you do not have a relation with this account."
                )

            logger.exception(
                f"response received: \n{resp.text}\nurl: {resp.url}\nstatus code: {resp.status_code}"
            )
            raise BadResponse("Received a non-200 response from Instagram")

        if parsed.get('error_type') == 'bad_password':
            raise IncorrectLoginDetails(
                "Instagram does not recognize the provided login details")
        if parsed.get('message') in ("checkpoint_required",
                                     "challenge_required"):
            if not hasattr(self, '_handle_challenge'):
                raise BadResponse(
                    "Challenge required. ChallengeMixin is not mixed in.")
            eh = self._handle_challenge(resp)
            if eh:
                return
        if parsed.get('message') == 'feedback_required':
            # TODO: implement a handler for this error
            raise BadResponse(
                "Something unexpected happened. Please check the IG app.")
        if parsed.get('message') == 'rate_limit_error':
            raise TimeoutError("Calm down. Please try again in a few minutes.")
        if parsed.get('message') == 'Not authorized to view user':
            raise AuthorizationError(
                "This is a private user, which you do not follow.")
        raise BadResponse("Received a non-200 response from Instagram")
Esempio n. 8
0
    def _handle_challenge(self, resp: requests.Response) -> bool:
        resp_data = resp.json()
        if resp_data['message'] not in ('challenge_required',
                                        'checkpoint_required'):
            raise BadResponse("Challenge required, but no URL provided.")

        api_path = resp_data['challenge']['api_path'][1:]
        _ = self._request(endpoint=api_path,
                          method=Method.GET,
                          query={
                              "guid": self.state.uuid,
                              "device_id": self.state.device_id
                          })
        # TODO: Add support for different kinds of challenges.
        #       Currently, only the verification pin challenge is supported, and other challenges, such as the
        #       'verify this was you', do not work.
        _ = self._request(
            endpoint=api_path,
            method=Method.POST,
            data={
                "choice":
                0,  # TODO: enum phone/email verification. Which value represent which one?
                "_csrftoken": self._session.cookies['csrftoken'],
                "_uuid": self.state.uuid,
                "bloks_versioning_id": self.state.bloks_version_id,
                "post": 1
            })
        security_code = input(
            "Verification needed. Type verification code here: ")
        _ = self._request(endpoint=api_path,
                          method=Method.POST,
                          data={
                              "_csrftoken": self._session.cookies['csrftoken'],
                              "_uuid": self.state.uuid,
                              "bloks_versioning_id":
                              self.state.bloks_version_id,
                              "post": 1,
                              "security_code": security_code
                          })
        return True
Esempio n. 9
0
 def _handle_checkpoint(self, resp: Response) -> bool:
     if not hasattr(self, '_handle_challenge'):
         raise BadResponse(
             "Challenge required. ChallengeMixin is not mixed in.")
     return self._handle_challenge(resp)