129 lines
4.4 KiB
Python
129 lines
4.4 KiB
Python
import os
|
|
import json
|
|
import requests
|
|
import logging
|
|
from django.http import JsonResponse, HttpResponse
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
from django.views.decorators.http import require_http_methods
|
|
from django.shortcuts import get_object_or_404
|
|
from pxy_openai.assistants import OpenAIAssistant
|
|
from .models import WhatsAppBot
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Utility Functions
|
|
def send_whatsapp_message(phone_number_id, sender_number, bot_response, graph_api_token):
|
|
"""
|
|
Sends a message back to the user via WhatsApp.
|
|
"""
|
|
try:
|
|
response = requests.post(
|
|
f"https://graph.facebook.com/v18.0/{phone_number_id}/messages",
|
|
headers={"Authorization": f"Bearer {graph_api_token}"},
|
|
json={
|
|
"messaging_product": "whatsapp",
|
|
"to": sender_number,
|
|
"text": {"body": bot_response},
|
|
},
|
|
)
|
|
response.raise_for_status()
|
|
except Exception as e:
|
|
logger.error(f"Error sending message via WhatsApp: {e}")
|
|
|
|
|
|
def verify_webhook_token(mode, token, challenge, verify_token):
|
|
"""
|
|
Verifies the webhook token and mode.
|
|
"""
|
|
if mode == "subscribe" and token == verify_token:
|
|
return HttpResponse(challenge, status=200)
|
|
return HttpResponse("Forbidden", status=403)
|
|
|
|
|
|
def parse_webhook_payload(payload):
|
|
"""
|
|
Parses the webhook payload and extracts relevant data.
|
|
"""
|
|
entry = payload.get("entry", [{}])[0]
|
|
changes = entry.get("changes", [{}])[0]
|
|
value = changes.get("value", {})
|
|
message = value.get("messages", [{}])[0]
|
|
return value, message
|
|
|
|
|
|
# Webhook Endpoint
|
|
@csrf_exempt
|
|
def webhook(request):
|
|
"""
|
|
Handles incoming webhook requests from WhatsApp.
|
|
"""
|
|
if request.method == "GET":
|
|
# Webhook verification
|
|
mode = request.GET.get("hub.mode")
|
|
token = request.GET.get("hub.verify_token")
|
|
challenge = request.GET.get("hub.challenge")
|
|
|
|
# Use the first active bot for verification
|
|
bot = WhatsAppBot.objects.filter(is_active=True).first()
|
|
if bot:
|
|
return verify_webhook_token(mode, token, challenge, bot.webhook_verify_token)
|
|
return HttpResponse("No active bots configured", status=500)
|
|
|
|
elif request.method == "POST":
|
|
try:
|
|
# Parse the incoming payload
|
|
payload = json.loads(request.body)
|
|
value, message = parse_webhook_payload(payload)
|
|
|
|
if message.get("type") == "text":
|
|
user_message = message["text"]["body"]
|
|
phone_number_id = value.get("metadata", {}).get("phone_number_id")
|
|
sender_number = message["from"]
|
|
|
|
logger.info(f"Received phone_number_id from webhook payload: {phone_number_id}")
|
|
# Fetch the appropriate bot configuration
|
|
bot = get_object_or_404(WhatsAppBot, phone_number_id=phone_number_id, is_active=True)
|
|
|
|
# Initialize the assistant and get a response
|
|
assistant = OpenAIAssistant(name=bot.assistant.name)
|
|
try:
|
|
bot_response = assistant.handle_message(user_message)
|
|
except Exception as e:
|
|
bot_response = f"Assistant error: {e}"
|
|
logger.error(bot_response)
|
|
|
|
# Send the response back to the user
|
|
send_whatsapp_message(bot.phone_number_id, sender_number, bot_response, bot.graph_api_token)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error processing webhook: {e}")
|
|
|
|
return JsonResponse({}, status=200)
|
|
|
|
return HttpResponse("Method Not Allowed", status=405)
|
|
|
|
|
|
# Webhook Verification Endpoint
|
|
@require_http_methods(["GET"])
|
|
def webhook_verification(request):
|
|
"""
|
|
Verifies the webhook token from WhatsApp.
|
|
"""
|
|
mode = request.GET.get("hub.mode")
|
|
token = request.GET.get("hub.verify_token")
|
|
challenge = request.GET.get("hub.challenge")
|
|
|
|
# Use the first active bot for verification
|
|
bot = WhatsAppBot.objects.filter(is_active=True).first()
|
|
if bot:
|
|
return verify_webhook_token(mode, token, challenge, bot.webhook_verify_token)
|
|
return HttpResponse("No active bots configured", status=500)
|
|
|
|
|
|
# Root Endpoint
|
|
def root(request):
|
|
"""
|
|
A root endpoint for basic connectivity testing.
|
|
"""
|
|
return HttpResponse("<pre>Nothing to see here.\nCheckout README.md to start.</pre>", content_type="text/html")
|