diff --git a/core/__pycache__/utils.cpython-311.pyc b/core/__pycache__/utils.cpython-311.pyc new file mode 100644 index 0000000..3e314ba Binary files /dev/null and b/core/__pycache__/utils.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 561d65f..773a0de 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/templates/core/dashboard.html b/core/templates/core/dashboard.html index f1402aa..854fe60 100644 --- a/core/templates/core/dashboard.html +++ b/core/templates/core/dashboard.html @@ -30,7 +30,7 @@
Today's Messages
-
0
+
{{ today_messages_count }}
@@ -107,4 +107,4 @@ -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/core/utils.py b/core/utils.py new file mode 100644 index 0000000..59f4d34 --- /dev/null +++ b/core/utils.py @@ -0,0 +1,119 @@ +import requests +import logging +from .models import Fanpage, Flow, Node, Edge, ChatSession, MessageLog + +logger = logging.getLogger(__name__) + +def send_fb_message(psid, access_token, message_content): + """ + Sends a message to a Facebook user via the Graph API. + message_content: dict matching Facebook's message object (e.g., {"text": "..."}) + """ + url = f"https://graph.facebook.com/v12.0/me/messages?access_token={access_token}" + payload = { + "recipient": {"id": psid}, + "message": message_content + } + try: + response = requests.post(url, json=payload) + response.raise_for_status() + return response.json() + except Exception as e: + logger.error(f"Error sending message to Facebook: {e}") + return None + +def get_next_node(session, message_text): + """ + Determines the next node in the flow based on user input. + """ + fanpage = session.fanpage + + if not session.current_node: + # Start of conversation: find the default flow and its start node + flow = Flow.objects.filter(fanpage=fanpage, is_default=True).first() + if not flow: + flow = Flow.objects.filter(fanpage=fanpage).first() + + if flow: + return Node.objects.filter(flow=flow, is_start_node=True).first() + return None + + # We are in an active session, look for a matching edge + edges = Edge.objects.filter(source_node=session.current_node) + message_text_clean = message_text.strip().lower() + + for edge in edges: + if edge.condition.lower() == message_text_clean: + return edge.target_node + + # If no matching edge, we might want to stay at the current node or find a global start + # For now, let's just return the current node (re-prompting) if it was a text node, + # or None if we don't know what to do. + return session.current_node + +def handle_webhook_event(data): + """ + Main entry point for processing Facebook webhook POST data. + """ + if data.get('object') != 'page': + return + + for entry in data.get('entry', []): + for messaging_event in entry.get('messaging', []): + sender_id = messaging_event.get('sender', {}).get('id') + recipient_id = messaging_event.get('recipient', {}).get('id') + + if not sender_id or not recipient_id: + continue + + # 1. Identify Fanpage + try: + fanpage = Fanpage.objects.get(page_id=recipient_id, is_active=True) + except Fanpage.DoesNotExist: + logger.warning(f"Received event for unknown or inactive Page ID: {recipient_id}") + continue + + # 2. Extract Message + message_text = "" + if 'message' in messaging_event: + message_text = messaging_event['message'].get('text', "") + elif 'postback' in messaging_event: + # Handle button clicks + message_text = messaging_event['postback'].get('payload', "") + + if not message_text: + continue + + # 3. Get or Create Session + session, created = ChatSession.objects.get_or_create( + psid=sender_id, + fanpage=fanpage + ) + + # 4. Log User Message + MessageLog.objects.create( + session=session, + sender_type='user', + message_text=message_text + ) + + # 5. Determine Next Node + next_node = get_next_node(session, message_text) + + if next_node: + # 6. Send Reply + # next_node.content is a JSONField, expected to be {"text": "..."} or similar + result = send_fb_message(sender_id, fanpage.access_token, next_node.content) + + if result: + # 7. Update Session + session.current_node = next_node + session.save() + + # 8. Log Bot Response + bot_text = next_node.content.get('text', '[Non-text message]') + MessageLog.objects.create( + session=session, + sender_type='bot', + message_text=bot_text + ) diff --git a/core/views.py b/core/views.py index 206641c..4bf003d 100644 --- a/core/views.py +++ b/core/views.py @@ -2,8 +2,10 @@ from django.shortcuts import render, redirect, get_object_or_404 from django.contrib.auth.decorators import login_required from django.views.decorators.csrf import csrf_exempt from django.http import HttpResponse +from django.utils import timezone import json from .models import Fanpage, Flow, MessageLog, ChatSession +from .utils import handle_webhook_event def home(request): if request.user.is_authenticated: @@ -16,11 +18,16 @@ def dashboard(request): flows = Flow.objects.all() recent_logs = MessageLog.objects.order_by('-timestamp')[:10] + # Count messages from today + today = timezone.now().date() + today_messages_count = MessageLog.objects.filter(timestamp__date=today).count() + context = { 'fanpage_count': fanpages.count(), 'flow_count': flows.count(), 'fanpages': fanpages, 'recent_logs': recent_logs, + 'today_messages_count': today_messages_count, } return render(request, 'core/dashboard.html', context) @@ -54,10 +61,11 @@ def webhook(request): # Handle incoming messages try: data = json.loads(request.body.decode('utf-8')) - # Process the webhook payload here in the future - # For now, just return 200 OK + # Process the webhook payload + handle_webhook_event(data) return HttpResponse('EVENT_RECEIVED') except Exception as e: + # Log the error if necessary return HttpResponse('Error processing request', status=400) return HttpResponse('Method not allowed', status=405) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index e22994c..d9ed9a4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ Django==5.2.7 mysqlclient==2.2.7 python-dotenv==1.1.1 +requests==2.31.0 \ No newline at end of file