From b8e064ceded613ad41a59adbda96f40168953c7b Mon Sep 17 00:00:00 2001 From: David Lev Date: Thu, 25 Apr 2024 01:51:27 +0300 Subject: [PATCH] [api] adding urls to facebook docs --- pywa/api.py | 147 +++++++++++++++++++++++++++++++++++++++++++------ pywa/client.py | 51 +++++++++-------- pywa/server.py | 2 +- 3 files changed, 159 insertions(+), 41 deletions(-) diff --git a/pywa/api.py b/pywa/api.py index a51ae8b..45540c7 100644 --- a/pywa/api.py +++ b/pywa/api.py @@ -13,7 +13,7 @@ class WhatsAppCloudApi: - """Internal methods for the WhatsApp client.""" + """Internal methods for the WhatsApp client. Do not use this class directly.""" def __init__( self, @@ -72,6 +72,8 @@ def get_app_access_token(self, app_id: int, app_secret: str) -> dict[str, str]: """ Get an access token for an app. + - Read more at `developers.facebook.com `_. + Return example:: { @@ -98,7 +100,7 @@ def get_app_access_token(self, app_id: int, app_secret: str) -> dict[str, str]: }, ) - def set_callback_url( + def set_app_callback_url( self, app_id: int, app_access_token: str, @@ -109,6 +111,8 @@ def set_callback_url( """ Set the callback URL for the webhook. + - Read more at `developers.facebook.com `_. + Return example:: { @@ -137,6 +141,68 @@ def set_callback_url( }, ) + def set_waba_callback_url( + self, + waba_id: str, + callback_url: str, + verify_token: str, + ) -> dict[str, bool]: + """ + Set an alternate callback URL on a WABA. + + - Read more at `developers.facebook.com `_. + + Return example:: + + { + 'success': True + } + + Args: + waba_id: The ID of the WhatsApp Business Account. + callback_url: The URL to set. + verify_token: The verify token to challenge the webhook with. + + Returns: + The success of the operation. + """ + return self._make_request( + method="POST", + endpoint=f"/{waba_id}/subscribed_apps", + json={ + "override_callback_uri": callback_url, + "verify_token": verify_token, + }, + ) + + def set_phone_callback_url( + self, + callback_url: str, + verify_token: str, + ) -> dict[str, bool]: + """ + Set an alternate callback URL on the business phone number. + + - Read more at `developers.facebook.com `_. + + Args: + callback_url: The URL to set. + verify_token: The verify token to challenge the webhook with. + + Returns: + The success of the operation. + """ + return self._make_request( + method="POST", + endpoint=f"/{self.phone_id}/", + json={ + "webhook_configuration": { + "override_callback_uri": callback_url, + "verify_token": verify_token, + } + }, + ) + def set_business_public_key( self, public_key: str, @@ -144,6 +210,8 @@ def set_business_public_key( """ Set the public key of the business. + - Read more at `developers.facebook.com `_. + Return example:: { @@ -171,6 +239,8 @@ def upload_media( """ Upload a media file to WhatsApp. + - Read more at `developers.facebook.com `_. + Return example:: { @@ -203,8 +273,8 @@ def upload_media( def get_media_url(self, media_id: str) -> dict: """ Get the URL of a media file. - - The url is valid for 5 minutes and can be downloaded only with access token. - - For more info: https://developers.facebook.com/docs/whatsapp/cloud-api/reference/media#retrieve-media-url + + - Read more at `developers.facebook.com `_. Return example:: @@ -233,6 +303,8 @@ def get_media_bytes( """ Get the bytes of a media file from WhatsApp servers. + - Read more at `developers.facebook.com `_. + Args: media_url: The URL of the media file (from ``get_media_url``). **kwargs: Additional arguments to pass to the request. @@ -250,6 +322,8 @@ def delete_media(self, media_id: str) -> dict[str, bool]: """ Delete a media file from WhatsApp servers. + - Read more at `developers.facebook.com `_. + Return example:: {'success': True} @@ -304,17 +378,19 @@ def send_message( typ: str, msg: dict[str, str | list[str]] | tuple[dict], reply_to_message_id: str | None = None, - tracker: str | None = None, + biz_opaque_callback_data: str | None = None, ) -> dict[str, dict | list]: """ Send a message to a WhatsApp user. + - Read more at `developers.facebook.com `_. + Args: to: The phone number to send the message to. typ: The type of the message (e.g. ``text``, ``image``, etc.). msg: The message object to send. reply_to_message_id: The ID of the message to reply to. - tracker: The tracker to send with the message. + biz_opaque_callback_data: The tracker to send with the message. Returns: The response from the WhatsApp Cloud API. @@ -327,8 +403,8 @@ def send_message( } if reply_to_message_id: data["context"] = {"message_id": reply_to_message_id} - if tracker: - data["biz_opaque_callback_data"] = tracker + if biz_opaque_callback_data: + data["biz_opaque_callback_data"] = biz_opaque_callback_data return self._make_request( method="POST", endpoint=f"/{self.phone_id}/messages", @@ -339,6 +415,10 @@ def register_phone_number( self, pin: str, data_localization_region: str = None ) -> dict[str, bool]: """ + Register a phone number. + + - Read more at `developers.facebook.com `_. + Return example: { 'success': True, @@ -366,6 +446,8 @@ def mark_message_as_read(self, message_id: str) -> dict[str, bool]: """ Mark a message as read. + - Read more at `developers.facebook.com `_. + Return example:: { @@ -394,6 +476,8 @@ def get_business_profile( """ Get the business profile. + - Read more at `developers.facebook.com `_. + Return example:: { @@ -432,6 +516,8 @@ def update_business_profile( """ Update the business profile. + - Read more at `developers.facebook.com `_. + Args: data: The data to update the business profile with. @@ -452,6 +538,8 @@ def get_commerce_settings(self) -> dict[str, list[dict]]: """ Get the commerce settings of the business catalog. + - Read more at `developers.facebook.com `_. + Return example:: { @@ -473,6 +561,8 @@ def update_commerce_settings(self, data: dict) -> dict[str, bool]: """ Change the commerce settings of the business catalog. + - Read more at `developers.facebook.com `_. + Args: data: The data to update the commerce settings with. @@ -490,14 +580,16 @@ def update_commerce_settings(self, data: dict) -> dict[str, bool]: def create_template( self, - business_account_id: str, + waba_id: str, template: dict[str, str | list[str]], ) -> dict[str, str]: """ Create a message template. + - Read more at `developers.facebook.com `_. + Args: - business_account_id: The ID of the business account. + waba_id: The ID of the WhatsApp Business Account. template: The template to create. Return example:: @@ -510,25 +602,29 @@ def create_template( """ return self._make_request( method="POST", - endpoint=f"/{business_account_id}/message_templates", + endpoint=f"/{waba_id}/message_templates", json=template, ) def create_flow( self, - business_account_id: str, + waba_id: str, name: str, categories: tuple[str, ...], clone_flow_id: str | None = None, + endpoint_uri: str | None = None, ) -> dict[str, str]: """ Create or clone a flow. + - Read more at `developers.facebook.com `_. + Args: - business_account_id: The ID of the business account. + waba_id: The ID of the WhatsApp Business Account. name: The name of the flow. categories: The categories of the flow. clone_flow_id: The ID of the flow to clone. + endpoint_uri: The endpoint URI of the flow. Return example:: @@ -540,10 +636,11 @@ def create_flow( "name": name, "categories": categories, **({"clone_flow_id": clone_flow_id} if clone_flow_id else {}), + **({"endpoint_uri": endpoint_uri} if endpoint_uri else {}), } return self._make_request( method="POST", - endpoint=f"/{business_account_id}/flows", + endpoint=f"/{waba_id}/flows", json=data, ) @@ -557,6 +654,8 @@ def update_flow_metadata( """ Update the metadata of a flow. + - Read more at `developers.facebook.com `_. + Args: flow_id: The ID of the flow. name: The name of the flow. @@ -584,6 +683,8 @@ def update_flow_json(self, flow_id: str, flow_json: str) -> dict: """ Update the JSON of a flow. + - Read more at `developers.facebook.com `_. + Args: flow_id: The ID of the flow. flow_json: The JSON of the flow. @@ -629,6 +730,8 @@ def publish_flow( """ Publish a flow. + - Read more at `developers.facebook.com `_. + Args: flow_id: The ID of the flow. @@ -650,6 +753,8 @@ def delete_flow( """ Delete a flow. + - Read more at `developers.facebook.com `_. + Args: flow_id: The ID of the flow. @@ -672,6 +777,8 @@ def deprecate_flow( """ Deprecate a flow. + - Read more at `developers.facebook.com `_. + Args: flow_id: The ID of the flow. @@ -695,6 +802,8 @@ def get_flow( """ Get a flow. + - Read more at `developers.facebook.com `_. + Args: flow_id: The ID of the flow. fields: The fields to get. @@ -732,14 +841,16 @@ def get_flow( def get_flows( self, - business_account_id: str, + waba_id: str, fields: tuple[str, ...] | None = None, ) -> dict[str, list[dict[str, Any]]]: """ Get all flows. + - Read more at `developers.facebook.com `_. + Args: - business_account_id: The ID of the business account. + waba_id: The ID of the WhatsApp Business Account. fields: The fields to get. Return example:: @@ -761,7 +872,7 @@ def get_flows( ] } """ - endpoint = f"/{business_account_id}/flows" + endpoint = f"/{waba_id}/flows" if fields: endpoint += f"?fields={','.join(fields)}" return self._make_request( @@ -776,6 +887,8 @@ def get_flow_assets( """ Get all assets of a flow. + - Read more at `developers.facebook.com `_. + Args: flow_id: The ID of the flow. diff --git a/pywa/client.py b/pywa/client.py index 9df3d46..8533581 100644 --- a/pywa/client.py +++ b/pywa/client.py @@ -35,6 +35,7 @@ FlowButton, ChatOpened, MessageType, + FlowStatus, ) from pywa.types.base_update import BaseUpdate from pywa.types.callback import CallbackDataT, CallbackData @@ -383,7 +384,7 @@ def send_message( typ=MessageType.TEXT.value, msg={"body": text, "preview_url": preview_url}, reply_to_message_id=reply_to_message_id, - tracker=_resolve_tracker_param(tracker), + biz_opaque_callback_data=_resolve_tracker_param(tracker), )["messages"][0]["id"] typ, kb = _resolve_buttons_param(buttons) return self.api.send_message( @@ -402,7 +403,7 @@ def send_message( footer=footer, ), reply_to_message_id=reply_to_message_id, - tracker=_resolve_tracker_param(tracker), + biz_opaque_callback_data=_resolve_tracker_param(tracker), )["messages"][0]["id"] send_text = send_message # alias @@ -476,7 +477,7 @@ def send_image( is_url=is_url, caption=caption, ), - tracker=_resolve_tracker_param(tracker), + biz_opaque_callback_data=_resolve_tracker_param(tracker), )["messages"][0]["id"] if not caption: raise ValueError( @@ -499,7 +500,7 @@ def send_image( footer=footer, ), reply_to_message_id=reply_to_message_id, - tracker=_resolve_tracker_param(tracker), + biz_opaque_callback_data=_resolve_tracker_param(tracker), )["messages"][0]["id"] def send_video( @@ -572,7 +573,7 @@ def send_video( is_url=is_url, caption=caption, ), - tracker=_resolve_tracker_param(tracker), + biz_opaque_callback_data=_resolve_tracker_param(tracker), )["messages"][0]["id"] if not caption: raise ValueError( @@ -595,7 +596,7 @@ def send_video( footer=footer, ), reply_to_message_id=reply_to_message_id, - tracker=_resolve_tracker_param(tracker), + biz_opaque_callback_data=_resolve_tracker_param(tracker), )["messages"][0]["id"] def send_document( @@ -671,7 +672,7 @@ def send_document( is_url=is_url, caption=caption, ), - tracker=_resolve_tracker_param(tracker), + biz_opaque_callback_data=_resolve_tracker_param(tracker), )["messages"][0]["id"] if not caption: raise ValueError( @@ -695,7 +696,7 @@ def send_document( footer=footer, ), reply_to_message_id=reply_to_message_id, - tracker=_resolve_tracker_param(tracker), + biz_opaque_callback_data=_resolve_tracker_param(tracker), )["messages"][0]["id"] def send_audio( @@ -740,7 +741,7 @@ def send_audio( media_id_or_url=audio, is_url=is_url, ), - tracker=_resolve_tracker_param(tracker), + biz_opaque_callback_data=_resolve_tracker_param(tracker), )["messages"][0]["id"] def send_sticker( @@ -787,7 +788,7 @@ def send_sticker( media_id_or_url=sticker, is_url=is_url, ), - tracker=_resolve_tracker_param(tracker), + biz_opaque_callback_data=_resolve_tracker_param(tracker), )["messages"][0]["id"] def send_reaction( @@ -830,7 +831,7 @@ def send_reaction( to=str(to), typ=MessageType.REACTION.value, msg={"emoji": emoji, "message_id": message_id}, - tracker=_resolve_tracker_param(tracker), + biz_opaque_callback_data=_resolve_tracker_param(tracker), )["messages"][0]["id"] def remove_reaction( @@ -870,7 +871,7 @@ def remove_reaction( to=str(to), typ=MessageType.REACTION.value, msg={"emoji": "", "message_id": message_id}, - tracker=_resolve_tracker_param(tracker), + biz_opaque_callback_data=_resolve_tracker_param(tracker), )["messages"][0]["id"] def send_location( @@ -916,7 +917,7 @@ def send_location( "name": name, "address": address, }, - tracker=_resolve_tracker_param(tracker), + biz_opaque_callback_data=_resolve_tracker_param(tracker), )["messages"][0]["id"] def request_location( @@ -941,7 +942,7 @@ def request_location( action={"name": "send_location"}, body=text, ), - tracker=_resolve_tracker_param(tracker), + biz_opaque_callback_data=_resolve_tracker_param(tracker), )["messages"][0]["id"] def send_contact( @@ -984,7 +985,7 @@ def send_contact( if isinstance(contact, Iterable) else (contact.to_dict(),), reply_to_message_id=reply_to_message_id, - tracker=_resolve_tracker_param(tracker), + biz_opaque_callback_data=_resolve_tracker_param(tracker), )["messages"][0]["id"] def send_catalog( @@ -1042,7 +1043,7 @@ def send_catalog( footer=footer, ), reply_to_message_id=reply_to_message_id, - tracker=_resolve_tracker_param(tracker), + biz_opaque_callback_data=_resolve_tracker_param(tracker), )["messages"][0]["id"] def send_product( @@ -1097,7 +1098,7 @@ def send_product( footer=footer, ), reply_to_message_id=reply_to_message_id, - tracker=_resolve_tracker_param(tracker), + biz_opaque_callback_data=_resolve_tracker_param(tracker), )["messages"][0]["id"] def send_products( @@ -1169,7 +1170,7 @@ def send_products( footer=footer, ), reply_to_message_id=reply_to_message_id, - tracker=_resolve_tracker_param(tracker), + biz_opaque_callback_data=_resolve_tracker_param(tracker), )["messages"][0]["id"] def mark_message_as_read( @@ -1573,7 +1574,7 @@ def create_template( self._validate_business_account_id_provided() return TemplateResponse( **self.api.create_template( - business_account_id=self.business_account_id, + waba_id=self.business_account_id, template=template.to_dict(placeholder=placeholder), ) ) @@ -1669,7 +1670,7 @@ def send_template( typ="template", msg=template.to_dict(is_header_url=is_url), reply_to_message_id=reply_to_message_id, - tracker=_resolve_tracker_param(tracker), + biz_opaque_callback_data=_resolve_tracker_param(tracker), )["messages"][0]["id"] def create_flow( @@ -1677,12 +1678,13 @@ def create_flow( name: str, categories: Iterable[FlowCategory | str], clone_flow_id: str | None = None, + endpoint_uri: str | None = None, ) -> str: """ Create a flow. - This method requires the WhatsApp Business account ID to be provided when initializing the client. - - New Flows are created in DRAFT status. + - New Flows are created in :class:`FlowStatus.DRAFT` status. - To update the flow json, use :py:func:`~pywa.client.WhatsApp.update_flow`. - To send a flow, use :py:func:`~pywa.client.WhatsApp.send_flow`. @@ -1690,6 +1692,8 @@ def create_flow( name: The name of the flow. categories: The categories of the flow. clone_flow_id: The flow ID to clone (optional). + endpoint_uri: The URL of the FlowJSON Endpoint. Starting from Flow 3.0 this property should be + specified only gere. Do not provide this field if you are cloning a Flow with version below 3.0. Example: @@ -1711,7 +1715,8 @@ def create_flow( name=name, categories=tuple(map(str, categories)), clone_flow_id=clone_flow_id, - business_account_id=self.business_account_id, + endpoint_uri=endpoint_uri, + waba_id=self.business_account_id, )["id"] def update_flow_metadata( @@ -1935,7 +1940,7 @@ def get_flows( return tuple( FlowDetails.from_dict(data=data, client=self) for data in self.api.get_flows( - business_account_id=self.business_account_id, + waba_id=self.business_account_id, fields=_get_flow_fields(invalidate_preview=invalidate_preview), )["data"] ) diff --git a/pywa/server.py b/pywa/server.py index 078f01b..89d1f46 100644 --- a/pywa/server.py +++ b/pywa/server.py @@ -167,7 +167,7 @@ def register_callback_url() -> None: app_id=app_id, app_secret=app_secret ) # noinspection PyProtectedMember - if not self.api.set_callback_url( + if not self.api.set_app_callback_url( app_id=app_id, app_access_token=app_access_token["access_token"], callback_url=full_url,