Skip to content

Commit

Permalink
Added to User: create_business, get_extended_access_token, get_access…
Browse files Browse the repository at this point in the history
…_token_debug_details

Added to Page: get_extended_access_token, get_access_token_debug_details
Added to AdAccount: create_agency(), PermittedTasks
Changed ProfilePictureSource.Type enums - now it is small, normal, album, large, square, as Facebook requests in their API
Fix: created get_shared_agencies() to call the badly named get_share_d_agencies() method
Added: Handling case where Facebook API returns an unformatted response, and a 'TypeError: string indices must be integers' is thrown
Rebased on latest v.3.2.4
Fixed names for methods in Pixel and Business classes that contain the word shared, that was converted to share_d for some reason
Fix: properly handling case where Facebook API returns an unformatted response
  • Loading branch information
advance512 committed Aug 14, 2023
1 parent a3113c3 commit 470722c
Show file tree
Hide file tree
Showing 9 changed files with 309 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1,4 +1,5 @@
*.pyc
.venv
MANIFEST
dist
build
Expand Down
2 changes: 1 addition & 1 deletion facebook_business/__init__.py
Expand Up @@ -21,7 +21,7 @@
from facebook_business.session import FacebookSession
from facebook_business.api import FacebookAdsApi

__version__ = '17.0.2'
__version__ = '2023.08.14'
__all__ = [
'session',
'objects',
Expand Down
47 changes: 47 additions & 0 deletions facebook_business/adobjects/adaccount.py
Expand Up @@ -4037,6 +4037,53 @@ def delete_users_of_any_audience(self, fields=None, params=None, batch=None, suc
self.assure_call()
return request.execute()

# --------------------------
# Mayple additions - start
# --------------------------

class PermittedTasks:
advertise = 'ADVERTISE'
analyze = 'ANALYZE'
draft = 'DRAFT'
manage = 'MANAGE'

def create_agency(self, fields=None, params=None, batch=None, success=None, failure=None, pending=False):
from facebook_business.utils import api_utils
if batch is None and (success is not None or failure is not None):
api_utils.warning('`success` and `failure` callback only work for batch call.')
param_types = {
'business': 'string',
'permitted_tasks': 'list<permitted_tasks_enum>',
}
enums = {
'permitted_tasks_enum': AdAccount.PermittedTasks.__dict__.values(),
}
request = FacebookRequest(
node_id=self['id'],
method='POST',
endpoint='/agencies',
api=self._api,
param_checker=TypeChecker(param_types, enums),
target_class=AdAccount,
api_type='EDGE',
response_parser=ObjectParser(target_class=AdAccount, api=self._api),
)
request.add_params(params)
request.add_fields(fields)

if batch is not None:
request.add_to_batch(batch, success=success, failure=failure)
return request
elif pending:
return request
else:
self.assure_call()
return request.execute()

# ------------------------
# Mayple additions - end
# ------------------------

_field_types = {
'account_id': 'string',
'account_status': 'unsigned int',
Expand Down
123 changes: 123 additions & 0 deletions facebook_business/adobjects/page.py
Expand Up @@ -4754,6 +4754,129 @@ def get_welcome_message_flows(self, fields=None, params=None, batch=None, succes
self.assure_call()
return request.execute()

def create_work_page_message(self, fields=None, params=None, batch=None, success=None, failure=None, pending=False):
from facebook_business.utils import api_utils
if batch is None and (success is not None or failure is not None):
api_utils.warning('`success` and `failure` callback only work for batch call.')
param_types = {
'message': 'Object',
'messaging_type': 'messaging_type_enum',
'notification_type': 'notification_type_enum',
'payload': 'string',
'persona_id': 'string',
'recipient': 'Object',
'sender_action': 'sender_action_enum',
'tag': 'Object',
}
enums = {
'messaging_type_enum': Page.MessagingType.__dict__.values(),
'notification_type_enum': Page.NotificationType.__dict__.values(),
'sender_action_enum': Page.SenderAction.__dict__.values(),
}
request = FacebookRequest(
node_id=self['id'],
method='POST',
endpoint='/workpagemessages',
api=self._api,
param_checker=TypeChecker(param_types, enums),
target_class=Page,
api_type='EDGE',
response_parser=ObjectParser(target_class=Page, api=self._api),
)
request.add_params(params)
request.add_fields(fields)

if batch is not None:
request.add_to_batch(batch, success=success, failure=failure)
return request
elif pending:
return request
else:
self.assure_call()
return request.execute()

# --------------------------
# Mayple additions - start
# --------------------------

def get_extended_access_token(self, fields=None, params=None, batch=None, pending=False):
param_types = {
'grant_type': 'string',
'client_id': 'string',
'client_secret': 'string',
'fb_exchange_token': 'string',
}
enums = {
}
request = FacebookRequest(
node_id='oauth',
method='GET',
endpoint='/access_token',
api=self._api,
param_checker=TypeChecker(param_types, enums),
target_class=AbstractCrudObject,
api_type='NODE',
response_parser=ObjectParser(reuse_object=self),
)

# TODO: Create an actual object instead of using AbstractCrudObject with this list..
request._accepted_fields = list(request._accepted_fields)
request._accepted_fields.extend([
'access_token', 'token_type'
])

request.add_params(params)
request.add_fields(fields)

if batch is not None:
request.add_to_batch(batch)
return request
elif pending:
return request
else:
self.assure_call()
return request.execute()

def get_access_token_debug_details(self, fields=None, params=None, batch=None, pending=False):
param_types = {
'input_token': 'string',
'access_token': 'string',
}
enums = {
}
request = FacebookRequest(
node_id='debug_token',
method='GET',
endpoint='/',
api=self._api,
param_checker=TypeChecker(param_types, enums),
target_class=AbstractCrudObject,
api_type='NODE',
response_parser=ObjectParser(reuse_object=self),
)

# TODO: Create an actual object instead of using AbstractCrudObject with this list..
request._accepted_fields = list(request._accepted_fields)
request._accepted_fields.extend([
'app_id', 'application', 'expires_at', 'is_valid', 'issued_at', 'scopes', 'user_id'
])

request.add_params(params)
request.add_fields(fields)

if batch is not None:
request.add_to_batch(batch)
return request
elif pending:
return request
else:
self.assure_call()
return request.execute()

# ------------------------
# Mayple additions - end
# ------------------------

_field_types = {
'about': 'string',
'access_token': 'string',
Expand Down
7 changes: 5 additions & 2 deletions facebook_business/adobjects/profilepicturesource.py
Expand Up @@ -49,9 +49,12 @@ class Field(AbstractObject.Field):
width = 'width'

class Type:
album = 'album'
small = 'small'
thumbnail = 'thumbnail'
small = 'small'
normal = 'normal'
album = 'album'
large = 'large'
square = 'square'

class BreakingChange:
profile_picture = 'PROFILE_PICTURE'
Expand Down
117 changes: 117 additions & 0 deletions facebook_business/adobjects/user.py
Expand Up @@ -2226,6 +2226,123 @@ def create_video(self, fields=None, params=None, batch=None, success=None, failu
self.assure_call()
return request.execute()

# --------------------------
# Mayple additions - start
# --------------------------

def create_business(self, fields=None, params=None, batch=None, pending=False):
from facebook_business.adobjects.business import Business

param_types = {
'name': 'string',
'vertical': 'vertical_enum',
'primary_page': 'int',
'timezone_id': 'unsigned int',
}
enums = {
'vertical_enum': Business.Vertical.__dict__.values(),
}
request = FacebookRequest(
node_id=self['id'],
method='POST',
endpoint='/businesses',
api=self._api,
param_checker=TypeChecker(param_types, enums),
target_class=Business,
api_type='EDGE',
response_parser=ObjectParser(target_class=Business, api=self._api),
)
request.add_params(params)
request.add_fields(fields)

if batch is not None:
request.add_to_batch(batch)
return request
elif pending:
return request
else:
self.assure_call()
return request.execute()

def get_extended_access_token(self, fields=None, params=None, batch=None, pending=False):
param_types = {
'grant_type': 'string',
'client_id': 'string',
'client_secret': 'string',
'fb_exchange_token': 'string',
}
enums = {
}
request = FacebookRequest(
node_id='oauth',
method='GET',
endpoint='/access_token',
api=self._api,
param_checker=TypeChecker(param_types, enums),
target_class=AbstractCrudObject,
api_type='NODE',
response_parser=ObjectParser(reuse_object=self),
)

# TODO: Create an actual object instead of using AbstractCrudObject with this list..
request._accepted_fields = list(request._accepted_fields)
request._accepted_fields.extend([
'access_token', 'token_type'
])

request.add_params(params)
request.add_fields(fields)

if batch is not None:
request.add_to_batch(batch)
return request
elif pending:
return request
else:
self.assure_call()
return request.execute()

def get_access_token_debug_details(self, fields=None, params=None, batch=None, pending=False):
param_types = {
'input_token': 'string',
'access_token': 'string',
}
enums = {
}
request = FacebookRequest(
node_id='debug_token',
method='GET',
endpoint='/',
api=self._api,
param_checker=TypeChecker(param_types, enums),
target_class=AbstractCrudObject,
api_type='NODE',
response_parser=ObjectParser(reuse_object=self),
)

# TODO: Create an actual object instead of using AbstractCrudObject with this list..
request._accepted_fields = list(request._accepted_fields)
request._accepted_fields.extend([
'app_id', 'application', 'expires_at', 'is_valid', 'issued_at', 'scopes', 'user_id', 'type',
'profile_id'
])

request.add_params(params)
request.add_fields(fields)

if batch is not None:
request.add_to_batch(batch)
return request
elif pending:
return request
else:
self.assure_call()
return request.execute()

# ------------------------
# Mayple additions - end
# ------------------------

_field_types = {
'about': 'string',
'age_range': 'AgeRange',
Expand Down
14 changes: 13 additions & 1 deletion facebook_business/api.py
Expand Up @@ -689,7 +689,19 @@ def execute(self):
if response.error():
raise response.error()
if self._response_parser:
return self._response_parser.parse_single(response.json())
try:
return self._response_parser.parse_single(response.json())
except TypeError as te:
if "string indices must be integers" in str(te):
raise FacebookRequestError(
"Erroneous response from Facebook API",
response._call,
response.status(),
response.headers(),
response.body(),
)
else:
raise
else:
return response

Expand Down
2 changes: 1 addition & 1 deletion facebook_business/apiconfig.py
Expand Up @@ -20,6 +20,6 @@

ads_api_config = {
'API_VERSION': 'v17.0',
'SDK_VERSION': 'v17.0.2',
'SDK_VERSION': 'v2023.08.14',
'STRICT_MODE': False
}
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -29,7 +29,7 @@
requirements_filename = os.path.join(this_dir, 'requirements.txt')

PACKAGE_NAME = 'facebook_business'
PACKAGE_VERSION = '17.0.3'
PACKAGE_VERSION = '2023.08.14'
PACKAGE_AUTHOR = 'Facebook'
PACKAGE_AUTHOR_EMAIL = ''
PACKAGE_URL = 'https://github.com/facebook/facebook-python-business-sdk'
Expand Down

0 comments on commit 470722c

Please sign in to comment.