Skip to content

Commit

Permalink
Merge pull request #61 from david-lev/solution-partners
Browse files Browse the repository at this point in the history
Solution partners - allowing to manage multiple whatsapp bots from the same client, and send messages from different numbers
  • Loading branch information
david-lev authored Jul 12, 2024
2 parents d727681 + 91b4f28 commit 96ab9ca
Show file tree
Hide file tree
Showing 12 changed files with 618 additions and 250 deletions.
2 changes: 1 addition & 1 deletion docs/source/content/updates/common_methods.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ Common methods
:members: id, raw, timestamp, stop_handling, continue_handling

.. autoclass:: BaseUserUpdate()
:members: sender, message_id_to_reply,
:members: sender, recipient, message_id_to_reply,
reply_text, reply_image, reply_video, reply_audio, reply_document, reply_location, reply_contact,
reply_sticker, reply_template, reply_catalog, reply_product, reply_products, react, unreact, mark_as_read
4 changes: 3 additions & 1 deletion docs/source/content/updates/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ All user-related-updates have common methods and properties:
* - Method / Property
- Description
* - :attr:`~BaseUserUpdate.sender`
- The user id who sent the update
- The phone id who sent the update
* - :attr:`~BaseUserUpdate.recipient`
- The phone id who received the update
* - :attr:`~BaseUserUpdate.message_id_to_reply`
- The message id to reply to
* - :meth:`~BaseUserUpdate.reply_text`
Expand Down
83 changes: 60 additions & 23 deletions pywa/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,11 @@ class WhatsAppCloudApi:

def __init__(
self,
phone_id: str,
token: str,
session: requests.Session,
base_url: str,
api_version: float,
):
self.phone_id = phone_id
self._session = self._setup_session(session, token)
self._base_url = f"{base_url}/v{api_version}"

Expand All @@ -42,7 +40,7 @@ def _setup_session(session, token: str) -> requests.Session:
return session

def __str__(self) -> str:
return f"WhatsAppCloudApi(phone_id={self.phone_id!r})"
return f"WhatsAppCloudApi(session={self._session})"

def __repr__(self) -> str:
return self.__str__()
Expand Down Expand Up @@ -180,13 +178,15 @@ def set_phone_callback_url(
self,
callback_url: str,
verify_token: str,
phone_id: str,
) -> dict[str, bool]:
"""
Set an alternate callback URL on the business phone number.
- Read more at `developers.facebook.com <https://developers.facebook.com/docs/whatsapp/embedded-signup/webhooks/override#set-phone-number-alternate-callback>`_.
Args:
phone_id: The ID of the phone number to set the callback URL on.
callback_url: The URL to set.
verify_token: The verify token to challenge the webhook with.
Expand All @@ -195,7 +195,7 @@ def set_phone_callback_url(
"""
return self._make_request(
method="POST",
endpoint=f"/{self.phone_id}/",
endpoint=f"/{phone_id}/",
json={
"webhook_configuration": {
"override_callback_uri": callback_url,
Expand All @@ -206,6 +206,7 @@ def set_phone_callback_url(

def set_business_public_key(
self,
phone_id: str,
public_key: str,
) -> dict[str, bool]:
"""
Expand All @@ -220,19 +221,21 @@ def set_business_public_key(
}
Args:
phone_id: The ID of the phone number to set the public key on.
public_key: The public key to set.
Returns:
The success of the operation.
"""
return self._make_request(
method="POST",
endpoint=f"/{self.phone_id}/whatsapp_business_encryption",
endpoint=f"/{phone_id}/whatsapp_business_encryption",
data={"business_public_key": public_key},
)

def upload_media(
self,
phone_id: str,
media: bytes,
mime_type: str,
filename: str,
Expand All @@ -249,6 +252,7 @@ def upload_media(
}
Args:
phone_id: The ID of the phone number to upload the media to.
media: media bytes or open(path, 'rb') object
mime_type: The type of the media file
filename: The name of the media file
Expand All @@ -257,7 +261,7 @@ def upload_media(
"""
return self._make_request(
method="POST",
endpoint=f"/{self.phone_id}/media",
endpoint=f"/{phone_id}/media",
files=[("file", (filename, media, mime_type))],
data={"messaging_product": "whatsapp"},
)
Expand Down Expand Up @@ -332,7 +336,6 @@ def send_raw_request(self, method: str, endpoint: str, **kwargs) -> Any:
Send a raw request to WhatsApp Cloud API.
- Use this method if you want to send a request that is not yet supported by pywa.
- The endpoint can contain path parameters (e.g. ``/{phone_id}/messages/``). only ``phone_id`` is supported.
- Every request will automatically include the ``Authorization`` and ``Content-Type`` headers. you can override them by passing them in ``kwargs`` (headers={...}).
Args:
Expand All @@ -359,12 +362,13 @@ def send_raw_request(self, method: str, endpoint: str, **kwargs) -> Any:
"""
return self._make_request(
method=method,
endpoint=endpoint.format(phone_id=self.phone_id),
endpoint=endpoint,
**kwargs,
)

def send_message(
self,
sender: str,
to: str,
typ: str,
msg: dict[str, str | list[str]] | tuple[dict],
Expand All @@ -377,6 +381,7 @@ def send_message(
- Read more at `developers.facebook.com <https://developers.facebook.com/docs/whatsapp/cloud-api/reference/messages>`_.
Args:
sender: The phone id to send the message from.
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.
Expand All @@ -399,12 +404,15 @@ def send_message(
data["biz_opaque_callback_data"] = biz_opaque_callback_data
return self._make_request(
method="POST",
endpoint=f"/{self.phone_id}/messages",
endpoint=f"/{sender}/messages",
json=data,
)

def register_phone_number(
self, pin: str, data_localization_region: str = None
self,
phone_id: str,
pin: str,
data_localization_region: str = None,
) -> dict[str, bool]:
"""
Register a phone number.
Expand All @@ -416,13 +424,18 @@ def register_phone_number(
'success': True,
}
Args:
phone_id: The ID of the phone number to register.
pin: The pin to register the phone number with.
data_localization_region: The region to localize the data (Value must be a 2-letter ISO 3166 country code (e.g. IN) indicating the country where you want data-at-rest to be stored).
Returns:
The success of the operation.
"""

return self._make_request(
method="POST",
endpoint=f"/{self.phone_id}/register",
endpoint=f"/{phone_id}/register",
json={
"messaging_product": "whatsapp",
"pin": pin,
Expand All @@ -434,7 +447,11 @@ def register_phone_number(
},
)

def mark_message_as_read(self, message_id: str) -> dict[str, bool]:
def mark_message_as_read(
self,
phone_id: str,
message_id: str,
) -> dict[str, bool]:
"""
Mark a message as read.
Expand All @@ -447,14 +464,15 @@ def mark_message_as_read(self, message_id: str) -> dict[str, bool]:
}
Args:
phone_id: The ID of the phone number that message belongs to.
message_id: The ID of the message to mark as read.
Returns:
The success of the operation.
"""
return self._make_request(
method="POST",
endpoint=f"/{self.phone_id}/messages",
endpoint=f"/{phone_id}/messages",
json={
"messaging_product": "whatsapp",
"status": "read",
Expand All @@ -463,7 +481,9 @@ def mark_message_as_read(self, message_id: str) -> dict[str, bool]:
)

def get_business_phone_number(
self, fields: tuple[str, ...] | None = None
self,
phone_id: str,
fields: tuple[str, ...] | None = None,
) -> dict[str, Any]:
"""
Get the business phone number.
Expand All @@ -483,19 +503,21 @@ def get_business_phone_number(
}
Args:
phone_id: The ID of the phone number to get.
fields: The fields to get.
Returns:
The business phone number.
"""
return self._make_request(
method="GET",
endpoint=f"/{self.phone_id}",
endpoint=f"/{phone_id}",
params={"fields": ",".join(fields)} if fields else None,
)

def update_conversational_automation(
self,
phone_id: str,
enable_welcome_message: bool | None = None,
prompts: tuple[dict] | None = None,
commands: str | None = None,
Expand All @@ -512,6 +534,7 @@ def update_conversational_automation(
}
Args:
phone_id: The ID of the phone number to update.
enable_welcome_message: Enable the welcome message.
prompts: The prompts (ice breakers) to set.
commands: The commands to set.
Expand All @@ -521,7 +544,7 @@ def update_conversational_automation(
"""
return self._make_request(
method="POST",
endpoint=f"/{self.phone_id}/conversational_automation",
endpoint=f"/{phone_id}/conversational_automation",
params={
k: v
for k, v in {
Expand All @@ -535,6 +558,7 @@ def update_conversational_automation(

def get_business_profile(
self,
phone_id: str,
fields: tuple[str, ...] | None = None,
) -> dict[str, list[dict[str, str | list[str]]]]:
"""
Expand All @@ -561,23 +585,25 @@ def get_business_profile(
}
Args:
phone_id: The ID of the phone number to get.
fields: The fields to get.
"""
return self._make_request(
method="GET",
endpoint=f"/{self.phone_id}/whatsapp_business_profile",
endpoint=f"/{phone_id}/whatsapp_business_profile",
params={"fields": ",".join(fields)} if fields else None,
)

def update_business_profile(
self, data: dict[str, str | list[str]]
self, phone_id: str, data: dict[str, str | list[str]]
) -> dict[str, bool]:
"""
Update the business profile.
- Read more at `developers.facebook.com <https://developers.facebook.com/docs/whatsapp/cloud-api/reference/business-profiles/#update-business-profile>`_.
Args:
phone_id: The ID of the phone number to update.
data: The data to update the business profile with.
Return example::
Expand All @@ -589,11 +615,11 @@ def update_business_profile(
data.update(messaging_product="whatsapp")
return self._make_request(
method="POST",
endpoint=f"/{self.phone_id}/whatsapp_business_profile",
endpoint=f"/{phone_id}/whatsapp_business_profile",
json=data,
)

def get_commerce_settings(self) -> dict[str, list[dict]]:
def get_commerce_settings(self, phone_id: str) -> dict[str, list[dict]]:
"""
Get the commerce settings of the business catalog.
Expand All @@ -610,19 +636,30 @@ def get_commerce_settings(self) -> dict[str, list[dict]]:
}
]
}
Args:
phone_id: The ID of the phone number to get.
Returns:
The commerce settings of the business catalog.
"""
return self._make_request(
method="GET",
endpoint=f"/{self.phone_id}/whatsapp_commerce_settings",
endpoint=f"/{phone_id}/whatsapp_commerce_settings",
)

def update_commerce_settings(self, data: dict) -> dict[str, bool]:
def update_commerce_settings(
self,
phone_id: str,
data: dict,
) -> dict[str, bool]:
"""
Change the commerce settings of the business catalog.
- Read more at `developers.facebook.com <https://developers.facebook.com/docs/whatsapp/cloud-api/guides/sell-products-and-services/set-commerce-settings>`_.
Args:
phone_id: The ID of the phone number to update.
data: The data to update the commerce settings with.
Return example::
Expand All @@ -633,7 +670,7 @@ def update_commerce_settings(self, data: dict) -> dict[str, bool]:
"""
return self._make_request(
method="POST",
endpoint=f"/{self.phone_id}/whatsapp_commerce_settings",
endpoint=f"/{phone_id}/whatsapp_commerce_settings",
params=data,
)

Expand Down
Loading

0 comments on commit 96ab9ca

Please sign in to comment.