Skip to content

Commit

Permalink
[docs] adding some instructions
Browse files Browse the repository at this point in the history
  • Loading branch information
david-lev committed Oct 19, 2023
1 parent 67f65a6 commit 43744d5
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 79 deletions.
9 changes: 7 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ ________________________
- Create a WhatsApp client, pass a web server app (Flask in this example) and start the webhook:

> See `Handlers <https://pywa.readthedocs.io/en/latest/content/handlers/overview.html>`_ for more information.

.. code-block:: python
from pywa import WhatsApp
Expand All @@ -96,9 +98,12 @@ ________________________
flask_app = Flask(__name__)
wa = WhatsApp(
phone_id='1234567890',
token='xxxxxxxxxxxxxxx',
token='xxxxxxx',
server=flask_app,
verify_token='XYZXYZ',
callback_url='https://6b3e-18.ngrok.io',
verify_token='XYZ123',
app_id=123456,
app_secret='yyyyyy'
)
@wa.on_message(text.matches('Hello', 'Hi'))
Expand Down
192 changes: 131 additions & 61 deletions docs/source/content/handlers/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,91 +2,161 @@
==================
.. currentmodule:: pywa.handlers

In order to handle the incoming updates from WhatsApp, you need to do two things:
- Start a web server that will receive the updates from the webhook
- Register a callback functions that will be called when an update is received
To handle the updates from WhatsApp, you need a way to receive them. This is done by starting a web server that
will receive the updates from WhatsApp and then register a callback functions that will be called when an update is received.

Let's see how to do that.

Starting a web server
---------------------
To allow maximum flexibility, ``pywa`` does not start the server. This allows the server to be
started independently with the desired configurations without any need for pywa to know them.
All pywa does is register a route that will handle the incoming updates from WhatsApp.
This means that you can use the same server to handle other parts of your application without any limitation from pywa.

.. role:: python(code)
:language: bash
In order for WhatsApp to send the updates to your server, you need a callback url.

The first thing you need to do is to start a web server that will receive the updates from WhatsApp.
The callback url must be a public, secure (HTTPS) url that points to your server (or your local machine if you are
testing locally). You can use a service like `ngrok <https://ngrok.com/>`_ , `localtunnel <https://localtunnel.github.io/www/>`_
or `serveo <https://serveo.net/>`_ to create a secure tunnel to which WhatsApp can send the updates. These services
will give you a public url that points to your machine (where you run the code).

.. note::
:class: dropdown
Here is an example using ngrok (https://ngrok.com/download)

To allow maximum flexibility, ``pywa`` does not initialize the server itself. This allows the server to be
initialized independently with the desired configurations without any need for pywa to know them.
All pywa does is register a route that will handle the incoming updates from WhatsApp.
This means that you can use the same server to handle other parts of your software without any limitation from pywa.
- You will get screen with the public url that points to your machine (The "Forwarding" line)

You first need to register webhook url to your WhatsApp app.
.. code-block:: bash
.. note::
:class: dropdown
ngrok http 8000
The webhook url must be a public, secure (HTTPS) url that points to your server (or your local machine if you are
testing locally). You can use a service like `ngrok <https://ngrok.com/>`_ , `localtunnel <https://localtunnel.github.io/www/>`_
or `serveo <https://serveo.net/>`_ to create a secure tunnel to which WhatsApp can send the updates. These services
will give you a public url that points to your machine (where you run the code).
Once you have a public url, You need to register it. This can be done two ways:

Once you have a public url, You need to start a web server that will receive the updates from WhatsApp.
* Automatically by pywa
* Manually in the WhatsApp App Dashboard

Here is an example using `Flask <https://flask.palletsprojects.com/>`_ (``pip3 install flask``):
Automatically registering the callback url
__________________________________________

.. code-block:: python
This is the easiest way to register the callback url. All you need to do is to pass the url to the ``callback_url`` argument
when initializing the WhatsApp client and ``pywa`` will automatically register the url, and handle the verification request
for you.

from flask import Flask
from pywa import WhatsApp
This method requires the ID and the secret of the WhatsApp app.
See `Here <https://developers.facebook.com/docs/development/create-an-app/app-dashboard/basic-settings/>`_ how to get them.

flask_app = Flask(__name__)
- Example using Flask

wa = WhatsApp(
phone_id='1234567890',
token='xxxxxxxxxxxxxxxxxxxx',
server=flask_app,
verify_token='XYZ123
)
.. toggle::

if __name__ == '__main__':
flask_app.run(host='localhost', port=8000) # or any other way to start a flask server (e.g. gunicorn, waitress, etc.)
- Install `Flask <https://flask.palletsprojects.com/>`_ (``pip3 install -U "pywa[flask]"``):

.. code-block:: python
:caption: main.py
:emphasize-lines: 9, 10, 11, 12, 13
And here is an example using `FastAPI <https://fastapi.tiangolo.com/>`_ (``pip3 install fastapi[uvicorn]``):
from flask import Flask
from pywa import WhatsApp
.. code-block:: python
flask_app = Flask(__name__)
import uvicorn
from fastapi import FastAPI
from pywa import WhatsApp
wa = WhatsApp(
phone_id='1234567890',
token='xxxxxx',
server=flask_app,
callback_url='https://12345678.ngrok.io',
verify_token='XYZ123',
app_id=123456,
app_secret='xxxxxx'
)
fastapi_app = FastAPI()
... # register the handlers
wa = WhatsApp(
phone_id='1234567890',
token='xxxxxxxxxxxxxxxxxxxx',
server=fastapi_app,
verify_token='XYZ123
)
if __name__ == '__main__':
# start the server with flask or gunicorn, waitress, etc.
flask_app.run(port=8000)
if __name__ == '__main__':
uvicorn.run(fastapi_app, host='localhost', port=8000) # or any other way to start a fastapi server
After you start the server, you can go to your WhatsApp app settings and register the webhook url.
This can be done in the ``App Dashboard > WhatsApp > Configuration > Callback URL``. You need to enter the webhook url
and the verify token that you used when initializing the WhatsApp client. When you click on ``Save``, WhatsApp will send
a ``GET`` request to the webhook url to verify that it is valid and that the verify token is correct (``pywa`` will
automatically handle this request and send the correct response to WhatsApp). If everything is correct, WhatsApp
will start sending the updates to the webhook url. In the next section we will see how to handle these updates.

.. image:: https://user-images.githubusercontent.com/42866208/260836608-aae9f5c2-0088-4332-9f92-78ce8917be56.png
:width: 100%
:alt: WhatsApp webhook configuration
The port that flask is running on (``8000`` in the example above) must be the same port that the callback url is listening on (e.g. ``ngrok http 8000``).

- Example using FastAPI

.. toggle::

- Install `FastAPI <https://fastapi.tiangolo.com/>`_ (``pip3 install -U "pywa[fastapi]"``):

.. code-block:: python
:caption: main.py
:emphasize-lines: 10, 11, 12, 13, 14
import uvicorn
from fastapi import FastAPI
from pywa import WhatsApp
fastapi_app = FastAPI()
wa = WhatsApp(
phone_id='1234567890',
token='xxxxxx',
server=fastapi_app,
callback_url='https://12345678.ngrok.io',
verify_token='XYZ123',
app_id=123456,
app_secret='xxxxxx'
)
... # register the handlers
if __name__ == '__main__':
# start the server with
uvicorn.run(fastapi_app, port=8000)
The port that fastapi is running on (``8000`` in the example above) must be the same port that the callback url is listening on (e.g. ``ngrok http 8000``).


--------------------------

Registering the callback url manually in the WhatsApp App Dashboard
___________________________________________________________________

In this method, pywa will not register the callback url for you. Instead, pywa will assume that you have already registered
an callback url, or that you will register one AFTER you start the server.

If you already have callback url that points to your server, you just need to start the server (on the same port that
the callback url is listening on).

If not, you will need to register a callback url manually in the WhatsApp App Dashboard, And this need to be done
AFTER you start the server, so pywa can handle the verification request from WhatsApp.

So, start the server:


The registration can be done in the ``App Dashboard > WhatsApp > Configuration > Callback URL``. You need to enter the webhook url
and the verify token that you used when initializing the WhatsApp client.

.. toggle::

.. image:: https://user-images.githubusercontent.com/42866208/260836608-aae9f5c2-0088-4332-9f92-78ce8917be56.png
:width: 100%
:alt: WhatsApp webhook configuration

.. important::

In this method, you must subscribe to webhook fields in your webhook settings. Otherwise, you will not receive any updates.
To enable it, go to your app dashboard, click on the ``Webhooks`` tab (Or the ``Configuration`` tab > ``Webhook fields``).
Then, subscribe to the fields you want to receive.

The current supported fields are:
- ``messages`` (all user related updates)
- ``message_template_status_update`` (template got approved, rejected, etc.)

You can subscribe to all the other fields, but they will not be handled by pywa, they can still be handled manually by
registering a callback for the ``on_raw_update`` decorator (or the ``RawUpdateHandler`` handler).

.. toggle::

.. image:: ../../../../_static/guides/webhook-fields.webp
:width: 600
:alt: Subscribe to webhook fields
:align: center

If everything is correct, WhatsApp will start sending the updates to the webhook url.

--------------------------

Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ authors = [
]
keywords = ['whatsapp', 'whatsapp-api', 'whatsapp-cloud-api', 'whatsapp-cloud', 'whatsapp-api-python',
'whatsapp-cloud-api-python', 'pywa', 'wapy', 'wa', 'wa-api', 'wa-cloud-api', 'wa-cloud', 'wa-api-python',
'wa-cloud-api-python'
'wa-cloud-api-python', 'whatsapp-webhook', 'whatsapp-webhook-python', 'whatsapp-webhook-api',
]
classifiers = [
'Topic :: Communications :: Chat',
Expand All @@ -39,7 +39,6 @@ flask = ['flask']
fastapi = ['fastapi', 'uvicorn[standard]']

[project.urls]
"Homepage" = "https://pywa.readthedocs.io/"
"Documentation" = "https://pywa.readthedocs.io/"
"Issue Tracker" = "https://github.com/david-lev/pywa/issues"
"Source Code" = "https://github.com/david-lev/pywa"
Expand Down
35 changes: 22 additions & 13 deletions pywa/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ def __init__(
session: requests.Session | None = None,
server: Flask | FastAPI | None = None,
webhook_endpoint: str = '/',
verify_token: str | None = None,
filter_updates: bool = True,
business_account_id: str | int | None = None,
callback_url: str | None = None,
fields: Iterable[str] | None = None,
app_id: int | None = None,
app_secret: str | None = None,
verify_token: str | None = None,
verify_timeout: int | None = None,
filter_updates: bool = True,
business_account_id: str | int | None = None,
) -> None:
"""
The WhatsApp client.
Expand All @@ -60,11 +60,14 @@ def __init__(
... phone_id="100944",
... token="EAADKQl9oJxx",
... server=flask_app,
... verify_token="my_verify_token",
... callback_url='https://6b3e.ngrok.io',
... verify_token="XYZ123",
... app_id=1234567890,
... app_secret="my_app_secret",
... )
>>> @wa.on_message()
... def message_handler(_: WhatsApp, msg: Message): print(msg)
>>> flask_app.run() # or by using a WSGI server (e.g. gunicorn, waitress, etc.)
>>> flask_app.run(port=8000)
Args:
phone_id: The Phone number ID (Not the phone number itself, the ID can be found in the App settings).
Expand All @@ -75,18 +78,24 @@ def __init__(
session: The session to use for requests (default: new ``requests.Session()``, Do not use the same
session across multiple WhatsApp clients!)
server: The Flask or FastAPI app instance to use for the webhook.
webhook_endpoint: The endpoint to listen for incoming messages (if you using the server for another purpose
and you're already using the ``/`` endpoint, you can change it to something else).
callback_url: The callback URL to register (optional, only if you want pywa to register the callback URL for
you).
verify_token: The verify token of the registered ``callback_url`` (Required when ``server`` is provided.
The verify token can be any string. It is used to challenge the webhook endpoint to verify that the
endpoint is valid).
fields: The fields to register for the callback URL (optional, if not provided, all supported fields will be
registered).
app_id: The ID of the WhatsApp App (optional, required when registering a ``callback_url``).
app_secret: The secret of the WhatsApp App (optional, required when registering a ``callback_url``).
verify_token: The verify token of the registered webhook (Required when ``server`` is provided).
filter_updates: Whether to filter out user updates that not sent to this phone_id (default: ``True``, does
registered. modify this if you want to reduce the number of unused requests to your server).
app_id: The ID of the app in the
`App Basic Settings <https://developers.facebook.com/docs/development/create-an-app/app-dashboard/basic-settings>`_
(optional, required when registering a ``callback_url``).
app_secret: The secret of the app in the
`App Basic Settings <https://developers.facebook.com/docs/development/create-an-app/app-dashboard/basic-settings>`_
(optional, required when registering a ``callback_url``).
webhook_endpoint: The endpoint to listen for incoming messages (if you're using the server for another purpose,
and you're already using the ``/`` endpoint, you can change it to something else).
filter_updates: Whether to filter out user updates that are not sent to this phone_id (default: ``True``, does
not apply to raw updates or updates that are not user-related).
business_account_id: The business account ID of the WhatsApp account (optional, required for some API
business_account_id: The business account ID that owns the app (optional, required for some API
methods).
"""
self.phone_id = str(phone_id)
Expand Down
2 changes: 1 addition & 1 deletion pywa/webhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def __init__(
if server is not None:
if not verify_token:
raise ValueError(
"When listening for incoming messages, a verify token must be provided.\n>> The verify token can "
"When listening for incoming updates, a verify token must be provided.\n>> The verify token can "
"be any string. It is used to challenge the webhook endpoint to verify that the endpoint is valid."
)
self._handlers: dict[type[Handler] | None, list[Callable[[WhatsApp, BaseUpdate | dict], Any]]] \
Expand Down

0 comments on commit 43744d5

Please sign in to comment.