WhatsApp Library
Independent whatsapp library
Pywce decouples the engine-processing logic and the core whatsapp module allowing you to use them independently.
In this section, we will show how to use it as just
another whatsapp client library
Import & setup dependencies
To get started, we will import our beloved dependencies and setup a basic fastapi app
import uvicorn
from fastapi import FastAPI, Request, Response, Query, Depends
import pywce
app = FastAPI()
wa_config = pywce.WhatsAppConfig(
token="<ACCESS-TOKEN>",
phone_number_id="<PHONE-NUMBER-ID>",
hub_verification_token="<WEBHOOK-VERIFICATION-TOKEN>"
)
whatsapp = pywce.WhatsApp(whatsapp_config=wa_config)
Webhook Challenge
To be able to receive notifications (webhooks) when a user chats with your chatbot, Meta requires that you complete & pass their verification challenge.
Pywce comes with "utility"
package to simplify some of the common tasks when working with WhatsApp Cloud Api
# ~ as before ~
@app.get("/webhook")
def verify_webhook(
mode: str = Query(...),
token: str = Query(...),
challenge: str = Query(...)
) -> Response:
"""
Verify WhatsApp webhook using query parameters.
:return: HTTP Response with challenge content if verification succeeds,
otherwise "Forbidden"
"""
result = whatsapp.util.verify_webhook_verification_challenge(
mode=mode, token=token, challenge=challenge
)
if result is None:
return Response(content="Forbidden", status_code=403)
return Response(content=result, status_code=200)
Handle Webhook payload
The business logic will be handled by this function - it takes whatsapp webhook data and chunks it in a way that is easier for you to work with.
The engine has utility methods to process some of the repititive tasks like verifying if sent message was a success, check if webhook payload is a valid message payload and much more
# ~ as before ~
@app.post("/webhook")
async def process_webhook(request: Request) -> Response:
"""
Handle incoming webhook events from WhatsApp and process them
:param request: FastAPI Request object containing the webhook payload
:return: HTTP Response with "ACK" content
"""
payload = await request.json()
headers = dict(request.headers)
if whatsapp.util.verify_webhook_payload(payload, headers):
if whatsapp.util.is_valid_webhook_message(payload):
# returns [WaUser] model
_user = whatsapp.util.get_wa_user(payload)
logger.info(f"Current whatsapp user: {_user}")
# returns [ResponseStructure] model
response = whatsapp.util.get_response_structure(payload)
logger.info(f"Webhook response structure: {response}")
match response.typ:
case pywce.MessageTypeEnum.TEXT:
result = whatsapp.send_message(
recipient_id=_user.wa_id,
message=f"You said: {response.body.get('body')}"
)
# TODO: implement other types and process them accordingly
case _:
result = whatsapp.send_message(
recipient_id=_user.wa_id,
message=f"Received whatsapp message type as: {response.typ}"
)
if whatsapp.util.was_request_successful(_user.wa_id, result):
return Response(content="Ack", status_code=200)
return Response(content="Err", status_code=400)
Library Models
The engine defines some models you will likely work with more.
The whatsapp.util.get_wa_user(payload)
call returns a WaUser
object defined as below
class WaUser:
# default whatsapp account user name
name: str = None
# whatsapp user mobile number
wa_id: str = None
# current message id
msg_id: str = None
timestamp: str = None
The ResponseStructure
response processes each webhook, determine the message type and its response payload
class ResponseStructure:
body: Any = None
typ: MessageTypeEnum = MessageTypeEnum.UNKNOWN
For example, text
messages usually have a response payload as below (Only showing the critical part)
"messages": [
{
"from": "16315551234",
"id": "wamid.ABGGFlCGg0cvAgo-sJQh43L5Pe4W",
"timestamp": "1603059201",
"text": {
"body": "Hello this is an answer"
},
"type": "text"
}
]
The ResponseStructure
will be as below
class ResponseStructure:
body: Any = {"body": "Hello this is an answer"}
typ: MessageTypeEnum = MessageTypeEnum.TEXT
Let's check a image message
"messages": [
{
"from": "15226273733",
"id": "wamid.8127ygd7d83",
"timestamp": "17727273",
"type": "image",
"image": {
"caption": "This is a caption",
"mime_type": "image/jpeg",
"sha256": "81d3bd8a8db4868c9520ed47186e8b7c5789e61ff79f7f834be6950b808a90d3",
"id": "2754859441498128"
}
}
]
The ResponseStructure
will be as below
class ResponseStructure:
body: Any = {
"caption": "This is a caption",
"mime_type": "image/jpeg",
"sha256": "81d3bd8a8db4868c9520ed47186e8b7c5789e61ff79f7f834be6950b808a90d3",
"id": "2754859441498128"
}
typ: MessageTypeEnum = MessageTypeEnum.IMAGE
I hope you get the idea of how the model will be like.
Happy chatbot development 😀