Пример #1
0
    def get_authorization_url(self, redirect_uri=None, scope=None, **kwargs):
        # type: (str, List, Dict) -> (str, str)
        """
        Build authorization url to do authorize.

        Refer: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow

        :param redirect_uri: The URL that you want to redirect the person logging in back to.
        Note: Your redirect uri need be set to `Valid OAuth redirect URIs` items in App Dashboard.
        :param scope: A list of Permissions to request from the person using your app.
        :param kwargs: Extend args for oauth.
        :return: Authorization url and state.
        """
        if not all([self.app_id, self.app_secret]):
            raise PyFacebookException(
                ErrorMessage(
                    code=ErrorCode.MISSING_PARAMS,
                    message="To do authorization need your app credentials"))

        session = self._get_oauth_session(redirect_uri=redirect_uri,
                                          scope=scope,
                                          **kwargs)

        authorization_url, state = session.authorization_url(
            url=self.authorization_url)

        return authorization_url, state
Пример #2
0
    def exchange_insights_token(self, page_id, access_token=None):
        # type: (str, Optional[str]) -> str
        """
        Use user access token to exchange page(managed by that user) access token.

        Refer:
            1. https://developers.facebook.com/docs/pages/access-tokens
            2. https://developers.facebook.com/docs/facebook-login/access-tokens

        :param page_id: The id for page
        :param access_token: user access token
        """
        if access_token is None:
            access_token = self._access_token

        args = {"access_token": access_token, "fields": "access_token"}
        resp = self._request(method="GET",
                             path="{version}/{page_id}".format(
                                 version=self.version, page_id=page_id),
                             args=args,
                             enforce_auth=False)

        data = self._parse_response(resp)
        if "access_token" not in data:
            raise PyFacebookException(
                ErrorMessage(
                    code=ErrorCode.INVALID_PARAMS,
                    message=
                    ("Can not change page access token. Confirm: \n"
                     "1. Your user access token has `page_show_list` or `manage_pages` permission.\n"
                     "2. You have the target page's manage permission.")))
        return data["access_token"]
Пример #3
0
    def _request(self,
                 path,
                 method="GET",
                 args=None,
                 post_args=None,
                 enforce_auth=True):
        # type: (str, str, Optional[dict], Optional[dict], bool) -> Response
        """
        Build the request and send request to Facebook.
        :param path: The path for resource on facebook.
        :param method: Http methods.
        :param args: GET parameters.
        :param post_args: POST parameters.
        :param enforce_auth: Set to True mean this request need access token.
        :return: The Response instance.
        """
        if args is None:
            args = dict()
        if post_args is not None:
            method = "POST"
        if enforce_auth:
            if method == "POST" and "access_token" not in post_args:
                post_args["access_token"] = self._access_token
            elif method == "GET" and "access_token" not in args:
                args["access_token"] = self._access_token

            # add appsecret_proof parameter
            # Refer: https://developers.facebook.com/docs/graph-api/securing-requests/
            if method == "POST" and "appsecret_proof" not in post_args:
                secret_proof = self._generate_secret_proof(
                    self.app_secret, post_args["access_token"])
                if secret_proof is not None:
                    post_args["appsecret_proof"] = secret_proof
            elif method == "GET" and "appsecret_proof" not in args:
                secret_proof = self._generate_secret_proof(
                    self.app_secret, args["access_token"])
                if secret_proof is not None:
                    args["appsecret_proof"] = secret_proof

        # check path
        if not path.startswith("https"):
            path = self.base_url + path
        try:
            response = self.session.request(method,
                                            path,
                                            timeout=self.__timeout,
                                            params=args,
                                            data=post_args,
                                            proxies=self.proxies)
        except requests.HTTPError as e:
            raise PyFacebookException(
                ErrorMessage(code=ErrorCode.HTTP_ERROR, message=e.args[0]))
        headers = response.headers
        self.rate_limit.set_limit(headers)
        if self.sleep_on_rate_limit:
            sleep_seconds = self.rate_limit.get_sleep_seconds(
                sleep_data=self.sleep_seconds_mapping)
            time.sleep(sleep_seconds)
        return response
Пример #4
0
    def testErrorMessage(self):
        error = ErrorMessage(code=ErrorCode.HTTP_ERROR, message="error")

        ex = PyFacebookException(error)

        self.assertEqual(ex.code, 10000)
        self.assertEqual(ex.message, "error")
        self.assertEqual(ex.error_type, "PyFacebookException")
Пример #5
0
    def get_page_info(self,
                      page_id=None,  # type: Optional[str]
                      username=None,  # type: Optional[str]
                      fields=None,  # type: Optional[Union[str, List, Tuple, Set]]
                      return_json=False  # type: bool
                      ):
        # type: (...) -> Union[Page, dict]
        """
        Retrieve the given page's basic info.

        :param page_id: The id for page.
        :param username: The username for page.
        :param fields:Comma-separated id string for data fields which you want.
        You can also pass this with an id list, tuple, set.
        :param return_json: Set to false will return instance of Page.
        Or return json data. Default is false.
        """
        if page_id:
            target = page_id
        elif username:
            target = username
        else:
            raise PyFacebookException(ErrorMessage(
                code=ErrorCode.MISSING_PARAMS,
                message="Specify at least one of page_id or username",
            ))
        if fields is None:
            fields = constant.FB_PAGE_FIELDS

        args = {
            "fields": enf_comma_separated("fields", fields)
        }

        resp = self._request(
            method='GET',
            path='{0}/{1}'.format(self.version, target),
            args=args
        )

        data = self._parse_response(resp)
        if return_json:
            return data
        else:
            return Page.new_from_json_dict(data)
Пример #6
0
    def get_pictures(
            self,
            ids,  # type: Optional[Union[str, List, Tuple, Set]]
            pic_type=None,  # type: Optional[str]
            return_json=False  # type: bool
    ):
        # type: (...) -> dict
        """
        :param ids: Comma-separated id(username) string for page which you want to get.
        You can also pass this with an id list, tuple, set.
        :param pic_type: The picture type.
        :param return_json: Set to false will return a dict of Comment instances.
        Or return json data. Default is false.
        """
        if pic_type is not None and pic_type not in constant.FB_PAGE_PICTURE_TYPE:
            raise PyFacebookException(
                ErrorMessage(
                    code=ErrorCode.INVALID_PARAMS,
                    message=
                    "For field picture: pic_type must be one of the following values: {}"
                    .format(', '.join(constant.FB_PAGE_PICTURE_TYPE))))

        args = {
            "ids": enf_comma_separated("ids", ids),
            'redirect': 0,  # if set 0 the api will return json response.
            'type': 'normal' if pic_type is None else pic_type,
        }
        resp = self._request(method='GET',
                             path='{0}/picture'.format(self.version),
                             args=args)

        data = self._parse_response(resp)

        res = {}
        for _id, p_data in iteritems(data):
            picture_data = p_data["data"]
            if return_json:
                res[_id] = picture_data
            else:
                res[_id] = ProfilePictureSource.new_from_json_dict(
                    picture_data)
        return res
Пример #7
0
    def get_picture(self,
                    page_id,  # type: str
                    pic_type=None,  # type: Optional[str]
                    return_json=False  # type: bool
                    ):
        # type: (...) -> Union[ProfilePictureSource, dict]
        """
        Retrieve the page's picture.

        :param page_id: The id for picture you want to retrieve data.
        :param pic_type: The picture type.
        :param return_json: Set to false will return a dict of Comment instances.
        Or return json data. Default is false.
        """

        if pic_type is not None and pic_type not in constant.FB_PAGE_PICTURE_TYPE:
            raise PyFacebookException(ErrorMessage(
                code=ErrorCode.INVALID_PARAMS,
                message="For field picture: pic_type must be one of the following values: {}".format(
                    ', '.join(constant.FB_PAGE_PICTURE_TYPE)
                )))

        args = {
            'redirect': 0,  # if set 0 the api will return json response.
            'type': 'normal' if pic_type is None else pic_type,
        }

        resp = self._request(
            method='GET',
            path='{0}/{1}/picture'.format(self.version, page_id),
            args=args
        )

        data = self._parse_response(resp)
        data = replace_from_keyword_in_json(data=data)

        if return_json:
            return data['data']
        else:
            return ProfilePictureSource.new_from_json_dict(data['data'])
Пример #8
0
    def exchange_access_token(self, response, return_json=False):
        # type: (str, bool) -> Union[AuthAccessToken, Dict]
        """
        :param response: The whole response url for your previous authorize step.
        :param return_json: Set to false will return instance of AuthAccessToken.
        Or return json data. Default is false.
        :return:
        """
        if self.auth_session is None:
            raise PyFacebookException(
                ErrorMessage(
                    code=ErrorCode.MISSING_PARAMS,
                    message="exchange token should do authorize first"))

        self.auth_session.fetch_token(self.exchange_access_token_url,
                                      client_secret=self.app_secret,
                                      authorization_response=response)

        self._access_token = self.auth_session.access_token

        if return_json:
            return self.auth_session.token
        else:
            return AuthAccessToken.new_from_json_dict(self.auth_session.token)
Пример #9
0
    def discovery_user_medias(
            self,
            username,  # type: str
            fields=None,  # type: Optional[Union[str, List, Tuple, Set]]
            since_time=None,  # type: Optional[str]
            until_time=None,  # type: Optional[str]
            count=10,  # type: Optional[int]
            limit=10,  # type: int
            return_json=False  # type: bool
    ):
        # type: (...) -> List[Union[IgProMedia, dict]]
        """
        Retrieve other business user's public medias.

        :param username: The username for other business user.
        :param fields: Comma-separated id string for data fields which you want.
                You can also pass this with an id list, tuple, set.
        :param since_time: Lower bound of the time range to the medias publish time.
                Format is %Y-%m-%d. If not provide, will not limit by this.
        :param until_time: Upper bound of the time range to the medias publish time.
                Format is %Y-%m-%d. If not provide, will not limit by this.
        :param count: The count is you want to retrieve medias. Default is 10.
                If you want to get all data. Set it to None.
                For now This may be not more than 10K.
        :param limit: Each request retrieve posts count from api.
                For medias it should no more than 500.
        :param return_json: Set to false will return a list instance of IgProMedia.
        Or return json data. Default is false.
        """

        try:
            if since_time is not None:
                since_time = datetime.datetime.strptime(since_time, '%Y-%m-%d')
            if until_time is not None:
                until_time = datetime.datetime.strptime(until_time, '%Y-%m-%d')
        except (ValueError, TypeError):
            raise PyFacebookException(
                ErrorMessage(
                    code=ErrorCode.INVALID_PARAMS,
                    message="since_time or until_time must format as %Y-%m-%d")
            )

        if count is not None:
            limit = min(limit, count)

        if fields is None:
            fields = constant.INSTAGRAM_MEDIA_PUBLIC_FIELD
        fields = enf_comma_separated("fields", fields)

        if (since_time is not None
                or until_time is not None) and "timestamp" not in fields:
            raise PyFacebookException(
                ErrorMessage(
                    code=ErrorCode.MISSING_PARAMS,
                    message=
                    "Use the since and until must give `timestamp` field"))

        args = {
            'path': '{0}/{1}'.format(self.version, self.instagram_business_id),
            'username': username,
            'limit': limit,
            "metric": enf_comma_separated("fields", fields),
        }

        medias = []
        next_cursor = None

        while True:
            next_cursor, previous_cursor, data = self.paged_by_cursor(
                args=args, next_cursor=next_cursor, business_discovery=True)
            data = data.get('data', [])
            # check if the media meet the request.
            for item in data:
                begin_flag, end_flag = True, True

                if "timestamp" in item:
                    timestamp = datetime.datetime.strptime(
                        item['timestamp'][:-5], '%Y-%m-%dT%H:%M:%S')
                    if since_time is not None:
                        begin_flag = since_time < timestamp
                    if until_time is not None:
                        end_flag = until_time > timestamp

                if all([begin_flag, end_flag]):
                    if return_json:
                        medias.append(item)
                    else:
                        medias.append(IgProMedia.new_from_json_dict(item))
                if not begin_flag:
                    next_cursor = None
                    break

            if count is not None:
                if len(medias) >= count:
                    medias = medias[:count]
                    break
            if next_cursor is None:
                break
        return medias
Пример #10
0
    def get_user_medias(
            self,
            user_id,  # type: str
            fields=None,  # type: Optional[Union[str, List, Tuple, Set]]
            since_time=None,  # type: Optional[str]
            until_time=None,  # type: Optional[str]
            count=10,  # type: Optional[int]
            limit=10,  # type: int
            return_json=False  # type: bool
    ):
        # type: (...) -> List[Union[IgProMedia, dict]]
        """
        Retrieve ig user medias data by user id.
        :param user_id: The id for instagram business user which you want to get data.
        :param fields: Comma-separated id string for data fields which you want.
                You can also pass this with an id list, tuple, set.
        :param since_time: Lower bound of the time range to the medias publish time.
                Format is %Y-%m-%d. If not provide, will not limit by this.
        :param until_time: Upper bound of the time range to the medias publish time.
                Format is %Y-%m-%d. If not provide, will not limit by this.
        :param count: The count for you want to get medias.
                Default is 10.
                If need get all, set this with None.
        :param limit: Each request retrieve medias count from api.
                For medias it should no more than 500.
        :param return_json: Set to false will return instance of IgProUser.
                Or return json data. Default is false.
        """

        try:
            if since_time is not None:
                since_time = datetime.datetime.strptime(since_time, '%Y-%m-%d')
            if until_time is not None:
                until_time = datetime.datetime.strptime(until_time, '%Y-%m-%d')
        except (ValueError, TypeError):
            raise PyFacebookException(
                ErrorMessage(
                    code=ErrorCode.INVALID_PARAMS,
                    message="since_time or until_time must format as %Y-%m-%d",
                ))

        if fields is None:
            fields = constant.INSTAGRAM_MEDIA_OWNER_FIELD

        if count is not None:
            limit = min(limit, count)

        args = {
            'fields': enf_comma_separated("fields", fields),
            'limit': limit
        }

        medias = []
        next_cursor = None

        while True:
            next_cursor, previous_cursor, data = self.paged_by_cursor(
                target=user_id,
                resource='media',
                args=args,
                next_cursor=next_cursor)
            data = data.get('data', [])
            for item in data:
                begin_flag, end_flag = True, True

                if "timestamp" in item:
                    timestamp = datetime.datetime.strptime(
                        item['timestamp'][:-5], '%Y-%m-%dT%H:%M:%S')
                    if since_time is not None:
                        begin_flag = since_time < timestamp
                    if until_time is not None:
                        end_flag = until_time > timestamp

                if all([begin_flag, end_flag]):
                    if return_json:
                        medias.append(item)
                    else:
                        medias.append(IgProMedia.new_from_json_dict(item))
                if not begin_flag:
                    next_cursor = None
                    break

            if count is not None:
                if len(medias) >= count:
                    medias = medias[:count]
                    break
            if next_cursor is None:
                break
        return medias
Пример #11
0
    def __init__(
            self,
            app_id=None,  # type: Optional[str]
            app_secret=None,  # type: Optional[str]
            short_token=None,  # type: Optional[str]
            long_term_token=None,  # type: Optional[str]
            application_only_auth=False,  # type: bool
            initial_access_token=True,  # type: bool
            version=None,  # type: Optional[str]
            timeout=None,  # type: Optional[int]
            sleep_on_rate_limit=False,  # type: bool
            sleep_seconds_mapping=None,  # type: Dict[int, int]
            proxies=None,  # type: Optional[dict]
            debug_http=False  # type: bool
    ):
        # type: (...) -> None
        """
        :param app_id: Your app id.
        :param app_secret: Your app secret.
        :param short_token: short-lived token
        :param long_term_token: long-lived token.
        :param application_only_auth: Use the `App Access Token` only.
        :param initial_access_token: If you want use api do authorize, set this with False.
        :param version: The version for the graph api.
        :param timeout: Request time out
        :param sleep_on_rate_limit: Use this will sleep between two request.
        :param sleep_seconds_mapping: Mapping for percent to sleep.
        :param proxies: Your proxies config.
        :param debug_http: Set to True to enable debug output from urllib when performing
        any HTTP requests.  Defaults to False.
        """
        self.app_id = app_id
        self.app_secret = app_secret
        self.short_token = short_token
        self.__timeout = timeout
        self.base_url = self.GRAPH_URL
        self.proxies = proxies
        self.session = requests.Session()
        self.sleep_on_rate_limit = sleep_on_rate_limit
        self.instagram_business_id = None
        self._debug_http = debug_http
        self.authorization_url = self.DEFAULT_AUTHORIZATION_URL
        self.exchange_access_token_url = self.DEFAULT_EXCHANGE_ACCESS_TOKEN_URL
        self.redirect_uri = self.DEFAULT_REDIRECT_URI
        self.scope = self.DEFAULT_SCOPE
        self.auth_session = None  # Authorization session
        self.rate_limit = RateLimit()

        if version is None:
            # default version is last new.
            self.version = self.VALID_API_VERSIONS[-1]
        else:
            version = str(version)
            if not version.startswith('v'):
                version = 'v' + version
            version_regex = re.compile(r"^v\d.\d{1,2}$")
            match = version_regex.search(str(version))
            if match is not None:
                if version not in self.VALID_API_VERSIONS:
                    raise PyFacebookException(
                        ErrorMessage(code=ErrorCode.INVALID_PARAMS,
                                     message="Valid API version are {}".format(
                                         ",".join(self.VALID_API_VERSIONS))))
                else:
                    self.version = version
            else:
                raise PyFacebookException(
                    ErrorMessage(
                        code=ErrorCode.INVALID_PARAMS,
                        message=
                        "Version string is invalid for {0}. You can provide with like: 5.0 or v5.0"
                        .format(version),
                    ))

        self.sleep_seconds_mapping = self._build_sleep_seconds_resource(
            sleep_seconds_mapping)

        if long_term_token:
            self._access_token = long_term_token
        elif short_token and all([self.app_id, self.app_secret]):
            token = self.get_long_token(app_id=self.app_id,
                                        app_secret=self.app_secret,
                                        short_token=self.short_token)
            self._access_token = token.access_token
        elif application_only_auth and all([self.app_id, app_secret]):
            token = self.get_app_token()
            self._access_token = token.access_token
        elif not initial_access_token and all([self.app_id, app_secret]):
            self._access_token = None
        else:
            raise PyFacebookException(
                ErrorMessage(
                    code=ErrorCode.MISSING_PARAMS,
                    message=
                    ("You can initial api with three methods: \n"
                     "1. Just provide long(short) lived token or app access token with param `long_term_token`.\n"
                     "2. Provide a short lived token and app credentials. Api will auto exchange long term token.\n"
                     "3. Provide app credentials and with application_only_auth set to true. "
                     "Api will auto get and use app access token.\n"
                     "4. Provide app credentials and prepare for do authorize(This will not retrieve access token)"
                     )))

        if debug_http:
            from six.moves import http_client
            http_client.HTTPConnection.debuglevel = 1

            logging.basicConfig(
            )  # you need to initialize logging, otherwise you will not see anything from requests
            logging.getLogger().setLevel(logging.DEBUG)
            requests_log = logging.getLogger("requests.packages.urllib3")
            requests_log.setLevel(logging.DEBUG)
            requests_log.propagate = True
 def exchange_insights_token(self, page_id, access_token=None):
     raise PyFacebookException(
         ErrorMessage(code=ErrorCode.NOT_SUPPORT_METHOD,
                      message="Method not support by this api."))
 def get_token_info(self, input_token=None, return_json=False):
     raise PyFacebookException(
         ErrorMessage(code=ErrorCode.NOT_SUPPORT_METHOD,
                      message="Method not support by this api."))