import json import logging import httpx from django.shortcuts import render, redirect from django.http import HttpResponse, JsonResponse from django.views.decorators.csrf import csrf_exempt from .models import Message, BotSettings from ai.local_ai_api import LocalAIApi logger = logging.getLogger(__name__) def index(request): messages = Message.objects.all().order_by('-timestamp')[:50] settings = BotSettings.objects.first() if not settings: settings = BotSettings.objects.create() context = { 'messages': messages, 'settings': settings, } return render(request, 'core/index.html', context) def settings_view(request): settings = BotSettings.objects.first() if not settings: settings = BotSettings.objects.create() if request.method == 'POST': settings.system_prompt = request.POST.get('system_prompt', settings.system_prompt) settings.is_active = 'is_active' in request.POST settings.verify_token = request.POST.get('verify_token', settings.verify_token) settings.whatsapp_access_token = request.POST.get('whatsapp_access_token', settings.whatsapp_access_token) settings.whatsapp_phone_number_id = request.POST.get('whatsapp_phone_number_id', settings.whatsapp_phone_number_id) settings.save() return redirect('index') return render(request, 'core/settings.html', {'settings': settings}) @csrf_exempt def get_pairing_code(request): if request.method != 'POST': return JsonResponse({'error': 'POST required'}, status=405) phone_number = request.POST.get('phone_number') if not phone_number: return JsonResponse({'error': 'Phone number required'}, status=400) # Call Bridge try: resp = httpx.post("http://127.0.0.1:3000/pair", json={"phoneNumber": phone_number}, timeout=30) if resp.status_code == 200: # Auto-configure settings for Bridge Mode settings = BotSettings.objects.first() if settings: settings.whatsapp_access_token = "BRIDGE" settings.whatsapp_phone_number_id = "BRIDGE" settings.save() return JsonResponse(resp.json()) else: return JsonResponse(resp.json(), status=resp.status_code) except Exception as e: return JsonResponse({'error': str(e)}, status=500) @csrf_exempt def webhook(request): if request.method == 'GET': # Meta Webhook verification mode = request.GET.get('hub.mode') token = request.GET.get('hub.verify_token') challenge = request.GET.get('hub.challenge') settings = BotSettings.objects.first() verify_token = settings.verify_token if settings else "my_secure_token_123" if mode and token: if mode == 'subscribe' and token == verify_token: logger.info("WEBHOOK_VERIFIED") return HttpResponse(challenge) else: return HttpResponse('Verification failed', status=403) return HttpResponse('Verification failed', status=403) elif request.method == 'POST': try: data = json.loads(request.body.decode('utf-8')) logger.info(f"Incoming WhatsApp data: {json.dumps(data)}") # Check if it's a message from WhatsApp (Meta or Bridge) if 'object' in data and data['object'] == 'whatsapp_business_account': for entry in data.get('entry', []): for change in entry.get('changes', []): value = change.get('value', {}) if 'messages' in value: for msg in value['messages']: sender_number = msg.get('from') message_body = msg.get('text', {}).get('body', '') if message_body and sender_number: process_whatsapp_message(sender_number, message_body) return JsonResponse({'status': 'ok'}) except Exception as e: logger.error(f"Error processing webhook: {str(e)}") return JsonResponse({'status': 'error', 'message': str(e)}, status=500) def process_whatsapp_message(sender_number, message_body): settings = BotSettings.objects.first() if not settings or not settings.is_active: return # Store incoming message db_msg = Message.objects.create( sender_number=sender_number, message_in=message_body ) # Call Gemini via AI Proxy prompt_input = [ {"role": "system", "content": settings.system_prompt}, {"role": "user", "content": message_body}, ] try: response = LocalAIApi.create_response({ "input": prompt_input, "model": "gemini-1.5-flash", }) if response.get("success"): ai_text = LocalAIApi.extract_text(response) db_msg.message_out = ai_text db_msg.save() # Send response back to WhatsApp send_whatsapp_message(sender_number, ai_text, settings) logger.info(f"Gemini response for {sender_number}: {ai_text}") else: logger.error(f"AI Proxy Error: {response.get('error')}") except Exception as e: logger.error(f"Error calling Gemini: {str(e)}") def send_whatsapp_message(to_number, text, settings): # Check for Bridge Mode if settings.whatsapp_access_token == "BRIDGE": try: with httpx.Client() as client: response = client.post( "http://127.0.0.1:3000/send", json={"to": to_number, "text": text}, timeout=10 ) if response.status_code == 200: logger.info(f"Sent via Bridge to {to_number}") else: logger.error(f"Bridge Send Failed: {response.text}") except Exception as e: logger.error(f"Bridge Exception: {e}") return # Standard Meta Cloud API Mode if not settings.whatsapp_access_token or not settings.whatsapp_phone_number_id: logger.warning("WhatsApp credentials missing in settings. Cannot send reply.") return url = f"https://graph.facebook.com/v17.0/{settings.whatsapp_phone_number_id}/messages" headers = { "Authorization": f"Bearer {settings.whatsapp_access_token}", "Content-Type": "application/json", } payload = { "messaging_product": "whatsapp", "to": to_number, "type": "text", "text": {"body": text}, } try: with httpx.Client() as client: response = client.post(url, headers=headers, json=payload, timeout=10) if response.status_code != 200: logger.error(f"Failed to send WhatsApp message: {response.text}") else: logger.info("WhatsApp message sent successfully.") except Exception as e: logger.error(f"Exception sending WhatsApp message: {str(e)}")