Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Whatsapp integration #2335

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions cookbook/examples/apps/whatsapp_chat_agent/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# WhatsApp Chat Agent with Stock Market Insights

This is a WhatsApp chatbot that provides stock market insights and financial advice using the WhatsApp Business API. The bot is built using FastAPI and can be run locally using ngrok for development and testing.

## Prerequisites

- Python 3.7+
- ngrok account (free tier works fine)
- WhatsApp Business API access
- Meta Developer account
- OpenAI API key

## Setup Instructions

1. **Install Dependencies**

```bash
pip install -r requirements.txt
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add the requirements.in and the generate_requirements script like we have with other apps

```

2. **Set up ngrok**
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe explain why ngrok is needed here? I know, but for users.


- Download and install ngrok from https://ngrok.com/download
- Sign up for a free account and get your authtoken
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Sign up for a free account and get your authtoken
- Sign up for a free account and get your auth-token

- Authenticate ngrok with your token:
```bash
ngrok config add-authtoken YOUR_AUTH_TOKEN
```

3. **Create a Meta Developer Account**

- Go to https://developers.facebook.com/
- Create a new app
- Set up WhatsApp in your app
- Get your WhatsApp Business Account ID and Phone Number ID

4. **Environment Variables**
Create a `.env` file in the project root with the following variables:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We generally prefer a .envrc file with export commands.


```env
WHATSAPP_ACCESS_TOKEN=your_whatsapp_access_token
WHATSAPP_PHONE_NUMBER_ID=your_phone_number_id
WHATSAPP_VERIFY_TOKEN=your_custom_verify_token # Can be any string you choose
OPENAI_API_KEY=your_openai_api_key
```

## Running the Application

1. **Start the FastAPI server**

```bash
python whatsapp_chat_agent.py
```

2. **Start ngrok**
In a new terminal window:

```bash
ngrok http 8000
```

3. **Configure Webhook**
- Copy the HTTPS URL provided by ngrok (e.g., https://xxxx-xx-xx-xxx-xx.ngrok.io)
- Go to your Meta Developer Portal
- Set up Webhooks for your WhatsApp Business Account
- Use the ngrok URL + "/webhook" as your Callback URL
- Use your WHATSAPP_VERIFY_TOKEN as the Verify Token
- Subscribe to the messages webhook

## Testing the Bot

1. Send a message to your WhatsApp Business number
2. The bot should respond with stock market insights based on your query
3. You can ask questions about:
- Stock prices
- Company information
- Analyst recommendations
- Stock fundamentals
- Historical prices
- Company news

## Troubleshooting

- Make sure all environment variables are properly set
- Check the FastAPI logs for any errors
- Verify that ngrok is running and the webhook URL is correctly configured
- Ensure your WhatsApp Business API is properly set up and the phone number is verified

## Important Notes

- The ngrok URL changes every time you restart ngrok (unless you have a paid account)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could create a static URL without paying? But that was a while ago

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, you can.

this is cursor written, i will change it soon.

- You'll need to update the Webhook URL in the Meta Developer Portal whenever the ngrok URL changes
- Keep your WHATSAPP_ACCESS_TOKEN and other credentials secure
- The bot stores conversation history in a SQLite database in the `tmp` directory
10 changes: 10 additions & 0 deletions cookbook/examples/apps/whatsapp_chat_agent/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
fastapi>=0.68.0
uvicorn>=0.15.0
python-dotenv>=0.19.0
requests>=2.26.0
yfinance>=0.1.63
openai>=1.0.0
agno>=0.1.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
agno>=0.1.0
agno>=1.1.9

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would actually have to be 1.1.10 after we release this, so the agent would have the right tools. Maybe don't specify a version, then it would be latest.

python-multipart>=0.0.5
aiohttp>=3.8.0
SQLAlchemy>=1.4.0
130 changes: 130 additions & 0 deletions cookbook/examples/apps/whatsapp_chat_agent/whatsapp_chat_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import logging
import os

from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.storage.agent.sqlite import SqliteAgentStorage
from agno.tools.whatsapp import WhatsAppTools
from agno.tools.yfinance import YFinanceTools
from dotenv import load_dotenv
from fastapi import FastAPI, HTTPException, Request
from fastapi.responses import PlainTextResponse

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Load environment variables
load_dotenv()

# Configure constants
VERIFY_TOKEN = os.getenv("WHATSAPP_VERIFY_TOKEN")
if not VERIFY_TOKEN:
raise ValueError("WHATSAPP_VERIFY_TOKEN must be set in .envrc")

WEBHOOK_URL = os.getenv("WHATSAPP_WEBHOOK_URL")
if not WEBHOOK_URL:
raise ValueError("WHATSAPP_WEBHOOK_URL must be set in .envrc")

AGENT_STORAGE_FILE = "tmp/whatsapp_agents.db"

# Initialize WhatsApp tools and agent
whatsapp = WhatsAppTools()
agent = Agent(
name="WhatsApp Assistant",
model=OpenAIChat(id="gpt-4o"),
tools=[
whatsapp,
YFinanceTools(
stock_price=True,
analyst_recommendations=True,
stock_fundamentals=True,
historical_prices=True,
company_info=True,
company_news=True,
),
],
storage=SqliteAgentStorage(table_name="whatsapp_agent", db_file=AGENT_STORAGE_FILE),
add_history_to_messages=True,
num_history_responses=3,
markdown=True,
description="You are a financial advisor and can help with stock-related queries. You will respond like how people talk to each other on whatsapp, with short sentences and simple language. don't add markdown to your responses.",
)

# Create FastAPI app
app = FastAPI()


@app.get("/webhook")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would define the app and the endpoints in another file. Like with other apps we have an app file and a agents file, just to make it easier to follow

async def verify_webhook(request: Request):
"""Handle WhatsApp webhook verification"""
mode = request.query_params.get("hub.mode")
token = request.query_params.get("hub.verify_token")
challenge = request.query_params.get("hub.challenge")

logger.info(f"Webhook verification request - Mode: {mode}, Token: {token}")

if mode == "subscribe" and token == VERIFY_TOKEN:
if not challenge:
raise HTTPException(status_code=400, detail="No challenge received")
return PlainTextResponse(content=challenge)

raise HTTPException(status_code=403, detail="Invalid verify token or mode")


@app.post("/webhook")
async def handle_message(request: Request):
"""Handle incoming WhatsApp messages"""
try:
body = await request.json()

# Validate webhook data
if body.get("object") != "whatsapp_business_account":
logger.warning(
f"Received non-WhatsApp webhook object: {body.get('object')}"
)
return {"status": "ignored"}

# Process messages
for entry in body.get("entry", []):
for change in entry.get("changes", []):
messages = change.get("value", {}).get("messages", [])

if not messages:
continue

message = messages[0]
if message.get("type") != "text":
continue

# Extract message details
phone_number = message["from"]
message_text = message["text"]["body"]

logger.info(f"Processing message from {phone_number}: {message_text}")

# Generate and send response
response = agent.run(message_text)
whatsapp.send_text_message_sync(
recipient=phone_number, text=response.content
)
logger.info(f"Response sent to {phone_number}")

return {"status": "ok"}

except Exception as e:
logger.error(f"Error processing webhook: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))


if __name__ == "__main__":
import uvicorn

logger.info("Starting WhatsApp Bot Server")
logger.info(f"Webhook URL: {WEBHOOK_URL}")
logger.info(f"Verify Token: {VERIFY_TOKEN}")
logger.info(
"Make sure your .env file contains WHATSAPP_ACCESS_TOKEN and WHATSAPP_PHONE_NUMBER_ID"
)
Comment on lines +126 to +128
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't put this here again


uvicorn.run(app, host="0.0.0.0", port=8000)
48 changes: 48 additions & 0 deletions cookbook/tools/whatsapp_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""
WhatsApp Cookbook
----------------

This cookbook demonstrates how to use WhatsApp integration with Agno. Before running this example,
you'll need to complete these setup steps:

1. Create Meta Developer Account
- Go to Meta Developer Portal (https://developers.facebook.com/) and create a new account
- Create a new app at Meta Apps Dashboard (https://developers.facebook.com/apps/)
- Enable WhatsApp integration for your app (https://developers.facebook.com/docs/whatsapp/cloud-api/get-started)

2. Set Up WhatsApp Business API
- Get your WhatsApp Business Account ID from Business Settings (https://business.facebook.com/settings/)
- Generate a permanent access token in System Users (https://business.facebook.com/settings/system-users)
- Set up a test phone number (https://developers.facebook.com/docs/whatsapp/cloud-api/get-started#testing-your-app)
- Create a message template in Meta Business Manager (https://business.facebook.com/wa/manage/message-templates/)

3. Configure Environment
- Set these environment variables:
WHATSAPP_ACCESS_TOKEN=your_access_token # Permanent access token from System Users
WHATSAPP_PHONE_NUMBER_ID=your_phone_number_id # Your WhatsApp test phone number ID

Important Notes:
- WhatsApp has a 24-hour messaging window policy
- You can only send free-form messages to users who have messaged you in the last 24 hours
- For first-time outreach, you must use pre-approved message templates
(https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-message-templates)
- Test messages can only be sent to numbers that are registered in your test environment

The example below shows how to send a template message using Agno's WhatsApp tools.
For more complex use cases, check out the WhatsApp Cloud API documentation:
https://developers.facebook.com/docs/whatsapp/cloud-api/overview
"""

from agno.agent import Agent
from agno.tools.whatsapp import WhatsAppTools

agent = Agent(
name="whatsapp",
tools=[WhatsAppTools()],
)

# Example: Send a template message
# Note: Replace 'hello_world' with your actual template name
agent.print_response(
"Send a template message using the 'hello_world' template in English"
)
Loading