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 from .models import WhatsAppBot, Conversation, Message from django.utils import timezone 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}") # 1) Fetch the active bot bot = get_object_or_404( WhatsAppBot, phone_number_id=phone_number_id, is_active=True ) # 2) Get or create Conversation conv, _ = Conversation.objects.get_or_create( bot=bot, user_number=sender_number, defaults={'started_at': timezone.now()} ) # 3) Save inbound message Message.objects.create( conversation=conv, direction="in", content=user_message ) # 4) Generate assistant response and measure time assistant = OpenAIAssistant(name=bot.assistant.name) start = timezone.now() try: bot_response = assistant.handle_message(user_message) except Exception as e: bot_response = f"Assistant error: {e}" logger.error(bot_response) end = timezone.now() # 5) Send the response back to the user send_whatsapp_message( phone_number_id, sender_number, bot_response, bot.graph_api_token ) # 6) Save outbound message with response time resp_ms = int((end - start).total_seconds() * 1000) Message.objects.create( conversation=conv, direction="out", content=bot_response, response_time_ms=resp_ms ) 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("
Nothing to see here.\nCheckout README.md to start.
", content_type="text/html") from django.contrib.auth.decorators import login_required from django.db.models import Avg from .models import Conversation, Message @login_required def whatsapp_stats(request): from django.utils import timezone since = timezone.now() - timezone.timedelta(days=1) total_convos = Conversation.objects.count() msgs_in = Message.objects.filter(direction="in", timestamp__gte=since).count() msgs_out = Message.objects.filter(direction="out", timestamp__gte=since).count() avg_rt = Message.objects.filter(direction="out", response_time_ms__isnull=False).aggregate(Avg("response_time_ms")) return JsonResponse({ "total_conversations": total_convos, "messages_in": msgs_in, "messages_out": msgs_out, "avg_response_time": avg_rt["response_time_ms__avg"] or 0, })