def exchange_page_access_token(self, page_id: str, access_token: Optional[str] = None) -> str: """ Get page access token by page administrator's 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: ID for page. :param access_token: Access token for user. :return: Page access token """ if access_token is None: access_token = self.access_token resp = self._request( url=f"{self.version}/{page_id}", args={ "fields": "access_token", "access_token": access_token }, auth_need=False, ) data = self._parse_response(resp) if "access_token" not in data: raise LibraryError({ "message": "Can not get page access token. Reason maybe: \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"]
def _get_oauth_session( self, redirect_uri: Optional[str] = None, scope: Optional[List[str]] = None, **kwargs, ) -> OAuth2Session: """ :param redirect_uri: The URL that you want to redirect the person logging in back to. :param scope: A list of permission string to request from the person using your app. :param kwargs: Additional parameters for oauth. :return: OAuth Session """ # check app credentials if not all([self.app_id, self.app_secret]): raise LibraryError({"message": "OAuth need your app credentials"}) if redirect_uri is None: redirect_uri = self.DEFAULT_REDIRECT_URI if scope is None: scope = self.DEFAULT_SCOPE session = OAuth2Session( client_id=self.app_id, scope=scope, redirect_uri=redirect_uri, state=self.STATE, **kwargs, ) session = facebook_compliance_fix(session) return session
def _request( self, url: str, args: Optional[dict] = None, post_args: Optional[dict] = None, verb: str = "GET", auth_need: bool = True, **kwargs, ) -> Response: """ :param url: Resource url for Graph. :param args: Query parameters. :param post_args: Form parameters. :param verb: HTTP method :param auth_need: Whether need access token. :param kwargs: Additional parameters. :return: """ if auth_need: if verb == "GET" or verb == "DELETE": args = self._append_token(args=args) elif verb == "POST": post_args = self._append_token(args=post_args) if not url.startswith("http"): url = self.base_url + url try: response = self.session.request( method=verb, url=url, timeout=self.__timeout, params=args, data=post_args, proxies=self.proxies, **kwargs, ) except requests.HTTPError as ex: raise LibraryError({"message": ex.args}) # check headers 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
def _parse_response(self, response: Response) -> dict: """ :param response: Response from graph api. :return: json data """ content_type = response.headers["Content-Type"] if "json" in content_type: data = response.json() self._check_graph_error(data=data) return data elif "image/" in content_type: data = { "data": response.content, "content-type": content_type, "url": response.url, } return data else: raise LibraryError( {"message": "Wrong response, not json or image"})
def test_error(): error = { "error": { "message": "Message describing the error", "type": "OAuthException", "code": 190, "error_subcode": 460, "error_user_title": "A title", "error_user_msg": "A message", "fbtrace_id": "EJplcsCHuLu", } } fb_err = FacebookError(error) assert fb_err.code == 190 assert "FacebookError" in repr(fb_err) error = {"message": "error message"} lib_err = LibraryError(error) assert lib_err.code == -1 assert "LibraryError" in str(lib_err)
def debug_token(self, input_token: str, access_token: Optional[str] = None) -> dict: raise LibraryError({"message": "Method not support"})
def get_app_token(self, app_id: Optional[str] = None, app_secret: Optional[str] = None) -> dict: raise LibraryError({"message": "Method not support"})
def exchange_long_lived_page_access_token( self, user_id: str, access_token: Optional[str] = None) -> dict: raise LibraryError({"message": "Method not support"})
def exchange_page_access_token(self, page_id: str, access_token: Optional[str] = None) -> str: raise LibraryError({"message": "Method not support"})
def __init__( self, app_id: Optional[str] = None, app_secret: Optional[str] = None, access_token: Optional[str] = None, application_only_auth: bool = False, oauth_flow: bool = False, version: Optional[str] = None, sleep_on_rate_limit: bool = True, sleep_seconds_mapping: Optional[Dict[int, int]] = None, base_url: Optional[str] = None, timeout: Optional[int] = None, proxies: Optional[dict] = None, instagram_business_id: Optional[str] = None, ): self.app_id = app_id self.app_secret = app_secret self.access_token = access_token self.session = requests.Session() self.__timeout = timeout self.proxies = proxies self.sleep_on_rate_limit = sleep_on_rate_limit self.sleep_seconds_mapping = self._build_sleep_seconds_resource( sleep_seconds_mapping=sleep_seconds_mapping) self.rate_limit = RateLimit() self.instagram_business_id = instagram_business_id # if provide url override self.base_url = self.GRAPH_URL if base_url is not None: self.base_url = base_url # version check 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 LibraryError({ "message": f"Valid API version are {','.join(self.VALID_API_VERSIONS)}" }) else: self.version = version else: raise LibraryError({ "message": f"Invalid version {version}. You can provide with like: 5.0 or v5.0" }) # Token if access_token: self.access_token = access_token elif application_only_auth and all([self.app_id, self.app_secret]): data = self.get_app_token() self.access_token = data["access_token"] elif oauth_flow and all([self.app_id, self.app_secret]): pass else: raise LibraryError({"message": "Need access token"})