
Security News
Open Source Maintainers Demand Ability to Block Copilot-Generated Issues and PRs
Open source maintainers are urging GitHub to let them block Copilot from submitting AI-generated issues and pull requests to their repositories.
py-whatsapp-cloudbot
Advanced tools
An asynchronous, easy-to-use Python library for interacting with the official WhatsApp Cloud API, inspired by the structure and ease of use of python-telegram-bot
.
This library provides an object-oriented interface to send messages, handle incoming webhooks using handlers and filters, manage media, and more, simplifying bot development for the WhatsApp Cloud platform.
asyncio
and httpx
for non-blocking I/O.Bot
, Application
, Handlers
) for clear code structure.filters.TEXT
, filters.IMAGE
, filters.Command
, filters.Regex
, logical operators & | ~
) to precisely target handlers.Make sure you have Python 3.8 or higher installed.
pip install py-whatsapp-cloudbot
This example demonstrates a simple bot that echoes back any text message it receives, using the FastAPI framework.
1. Prerequisites:
2. Environment Variables:
Create a .env
file in your project directory (e.g., inside examples/
) to store your credentials securely. The library (using python-dotenv
in the example) will load these.
# examples/.env.example
WHATSAPP_TOKEN="YOUR_WHATSAPP_API_TOKEN"
PHONE_NUMBER_ID="YOUR_PHONE_NUMBER_ID"
VERIFY_TOKEN="YOUR_CHOSEN_WEBHOOK_VERIFY_TOKEN" # A secret string you create
# PORT=5000 # Optional: Defaults to 5000 in the example
WHATSAPP_TOKEN
: Your API access token.PHONE_NUMBER_ID
: The ID of the phone number sending messages.VERIFY_TOKEN
: A secret string you define. You'll need this when setting up the webhook in the Meta Developer Dashboard.3. Example Code (echo_bot.py
):
# examples/echo_bot.py
import logging
import os
import asyncio
from pathlib import Path
from dotenv import load_dotenv
# --- Logging and Environment Setup ---
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
script_dir = Path(__file__).parent
dotenv_path = script_dir / '.env'
if dotenv_path.is_file():
load_dotenv(dotenv_path=dotenv_path)
logger.info(f"Loaded environment variables from: {dotenv_path}")
else:
logger.warning(f".env file not found at: {dotenv_path}")
# --- Imports ---
try:
from fastapi import FastAPI
except ImportError:
logger.error("FastAPI not found. Install with: pip install 'py-whatsapp-cloudbot[fastapi]'")
exit(1)
try:
from wa_cloud import (
Application, Bot, Message, MessageHandler, filters, WhatsAppError, APIError,
MessageType # Import Enums used
)
from wa_cloud.webhooks import setup_fastapi_webhook
except ImportError as e:
logger.error(f"Failed to import wa_cloud components. Install with `pip install -e .` or `pip install py-whatsapp-cloudbot`: {e}")
exit(1)
# --- Configuration ---
WHATSAPP_TOKEN = os.getenv("WHATSAPP_TOKEN")
PHONE_NUMBER_ID = os.getenv("PHONE_NUMBER_ID")
VERIFY_TOKEN = os.getenv("VERIFY_TOKEN")
WEBHOOK_PATH = "/webhook"
if not all([WHATSAPP_TOKEN, PHONE_NUMBER_ID, VERIFY_TOKEN]):
logger.critical("Missing required environment variables. Check .env file.")
exit(1)
# --- Bot Logic ---
async def echo_handler(message: Message, bot: Bot):
"""Handles incoming text messages (excluding commands) and echoes them."""
if message.text: # Ensure text object exists
sender_id = message.chat_id
received_text = message.text.body
logger.info(f"Received text from {sender_id}: '{received_text}'")
try:
# Mark as read and show typing (optional, good UX)
await bot.mark_as_read(message.id, show_typing=True)
# Add a small delay to simulate processing
await asyncio.sleep(1)
# Send the echo reply
await bot.send_text(to=sender_id, text=f"Echo: {received_text}")
logger.info(f"Echo sent to {sender_id}")
except APIError as e:
logger.error(f"API Error sending echo to {sender_id}: {e}")
except WhatsAppError as e:
logger.error(f"Library Error sending echo to {sender_id}: {e}")
except Exception as e:
logger.exception(f"Unexpected error in echo_handler for {sender_id}")
async def start_command_handler(message: Message, bot: Bot):
"""Handles the /start command."""
logger.info(f"Received /start command from {message.chat_id}")
await bot.send_text(message.chat_id, "Hello! I'm an echo bot using wa_cloud.")
# --- Application Setup ---
def setup_app() -> FastAPI:
"""Configures and returns the FastAPI application."""
logger.info("Setting up wa_cloud application...")
bot = Bot(token=WHATSAPP_TOKEN, phone_number_id=PHONE_NUMBER_ID)
application = Application(bot=bot)
# Register handlers
application.add_handler(MessageHandler(filters.Command("start"), start_command_handler))
# Use ~filters.ANY_COMMAND to exclude commands from the echo handler
application.add_handler(MessageHandler(filters.TEXT & ~filters.ANY_COMMAND, echo_handler))
logger.info("Handlers registered.")
# Create FastAPI app and setup webhook routes
fastapi_app = FastAPI(title="wa_cloud Echo Bot")
setup_fastapi_webhook(
app=fastapi_app,
application=application,
webhook_path=WEBHOOK_PATH,
verify_token=VERIFY_TOKEN,
run_background_tasks=True # Recommended
)
logger.info("FastAPI webhook configured.")
return fastapi_app
# --- Run the App ---
app = setup_app() # Initialize the app when the script loads
# Allow running with `python echo_bot.py`
if __name__ == "__main__":
import uvicorn
logger.info("Starting Uvicorn server for development...")
port = int(os.getenv("PORT", 5000))
uvicorn.run(
"echo_bot:app", # Point to the app object in this file
host="0.0.0.0", # Listen on all interfaces
port=port,
reload=True # Enable auto-reload for development
)
4. Run Your Bot Script:
Before configuring the webhook, your bot server needs to be running to respond to Meta's verification request.
echo_bot.py
(or run from the root if imports are set up correctly for that).# If echo_bot.py is inside examples/ and you are in the root directory:
uvicorn examples.echo_bot:app --reload --port 5000
# If you are inside examples/:
# uvicorn echo_bot:app --reload --port 5000
http://127.0.0.1:5000
).5. Expose Your Bot with Ngrok:
Your local server needs a public URL.
ngrok http 5000
https://<id>.ngrok-free.app
).6. Configure Meta Webhook:
Tell WhatsApp where to send events.
WEBHOOK_PATH
(e.g., https://<id>.ngrok-free.app/webhook
).VERIFY_TOKEN
string from your .env
file.GET /webhook
request and a "Webhook verification successful" log message.messages
is subscribed.7. Test:
/start
.Bot
(wa_cloud.Bot
): Handles communication to the WhatsApp API (sending messages, uploading media). Requires token and phone number ID.Application
(wa_cloud.Application
): Orchestrates incoming updates. It holds the Bot
instance and a list of handlers. It processes webhook payloads and dispatches updates.wa_cloud.MessageHandler
): Define how to react to updates. You register these with the Application
. They consist of filters and a callback function.wa_cloud.filters
): Determine if a handler should process an update based on criteria (message type, text content, command, etc.). Combine filters using &
(AND), |
(OR), ~
(NOT).wa_cloud.models
): Pydantic models representing WhatsApp objects (Message
, Contact
, etc.) for data validation and easier access.All send_*
methods are asynchronous and available on the Bot
instance.
import wa_cloud
from wa_cloud.models import MediaBase # For media headers/params
# Assume 'bot' is an initialized wa_cloud.Bot instance
# Assume 'chat_id' is the recipient's WA ID (e.g., "16505551234")
# Send Text
await bot.send_text(chat_id, "Simple text message.")
await bot.send_text(chat_id, "*Bold* and _italic_ text.", preview_url=False)
# Send Image (using previously uploaded media ID)
try:
# response = await bot.upload_media("path/to/image.jpg")
# media_id = response.id
media_id = "YOUR_UPLOADED_IMAGE_MEDIA_ID" # Replace
if media_id:
await bot.send_image(chat_id, media_id=media_id, caption="Optional caption.")
except Exception as e:
print(f"Error sending image: {e}")
# Send Document (using link - not recommended for performance)
# await bot.send_document(
# chat_id,
# link="https://www.example.com/report.pdf",
# filename="Annual Report.pdf",
# caption="See attached report."
# )
# Send Location
# await bot.send_location(chat_id, latitude=..., longitude=..., name="...")
# Send Reaction
# await bot.send_reaction(chat_id, message_id="wamid_of_target_message", emoji="👍")
(See the full_test_bot.py
example in the repository for more sending methods like interactive messages, templates, contacts, etc.)
Create async
functions (callbacks) and register them with MessageHandler
using appropriate filters
.
from wa_cloud import filters, Message, Bot, MessageType
async def handle_images(message: Message, bot: Bot):
if message.image: # Check if image object exists
print(f"Received image! Media ID: {message.media_id}")
await bot.send_text(message.chat_id, f"Got your image! Caption: {message.caption or 'None'}")
await bot.mark_as_read(message.id)
async def handle_order_command(message: Message, bot: Bot):
# Example: /order 12345
match = re.match(r"/order\s+(\d+)", message.text.body)
if match:
order_id = match.group(1)
print(f"Processing order command for ID: {order_id}")
await bot.send_text(message.chat_id, f"Looking up order {order_id}...")
await bot.mark_as_read(message.id)
# In your setup:
# application.add_handler(MessageHandler(filters.IMAGE, handle_images))
# application.add_handler(MessageHandler(filters.Command("order"), handle_order_command))
API calls made via the Bot
instance can raise exceptions defined in wa_cloud.error
. Wrap calls in try...except
blocks.
from wa_cloud import APIError, NetworkError, WhatsAppError
try:
await bot.send_text(chat_id, "Risky message!")
except APIError as e:
logger.error(f"API Error: {e} - Response Data: {e.response_data}")
except NetworkError as e:
logger.error(f"Network Error: {e}")
# Maybe implement retry logic
except WhatsAppError as e:
logger.error(f"Library Error: {e}")
except Exception as e:
logger.exception("An unexpected error occurred.")
Contributions are welcome! Please feel free to open an issue or submit a pull request on the GitHub repository.
You may copy, modify, and distribute this software under the terms of the LGPL-3. Any modifications or derivative works must also be licensed under LGPL‑3, but applications that merely use or link to the library are exempt from its requirements.
FAQs
An asynchronous Python library for the WhatsApp Cloud API.
We found that py-whatsapp-cloudbot demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Open source maintainers are urging GitHub to let them block Copilot from submitting AI-generated issues and pull requests to their repositories.
Research
Security News
Malicious Koishi plugin silently exfiltrates messages with hex strings to a hardcoded QQ account, exposing secrets in chatbots across platforms.
Research
Security News
Malicious PyPI checkers validate stolen emails against TikTok and Instagram APIs, enabling targeted account attacks and dark web credential sales.