This commit is contained in:
Flatlogic Bot 2026-02-07 18:16:19 +00:00
parent 5d275d310a
commit 84f67bc019
6 changed files with 132 additions and 4 deletions

Binary file not shown.

View File

@ -30,7 +30,7 @@
<div class="col-md-4"> <div class="col-md-4">
<div class="card p-4 text-center"> <div class="card p-4 text-center">
<h6 class="text-uppercase text-muted small fw-bold">Today's Messages</h6> <h6 class="text-uppercase text-muted small fw-bold">Today's Messages</h6>
<div class="display-4 fw-bold text-coral">0</div> <div class="display-4 fw-bold text-coral">{{ today_messages_count }}</div>
</div> </div>
</div> </div>
</div> </div>
@ -107,4 +107,4 @@
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

119
core/utils.py Normal file
View File

@ -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
)

View File

@ -2,8 +2,10 @@ from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse from django.http import HttpResponse
from django.utils import timezone
import json import json
from .models import Fanpage, Flow, MessageLog, ChatSession from .models import Fanpage, Flow, MessageLog, ChatSession
from .utils import handle_webhook_event
def home(request): def home(request):
if request.user.is_authenticated: if request.user.is_authenticated:
@ -16,11 +18,16 @@ def dashboard(request):
flows = Flow.objects.all() flows = Flow.objects.all()
recent_logs = MessageLog.objects.order_by('-timestamp')[:10] 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 = { context = {
'fanpage_count': fanpages.count(), 'fanpage_count': fanpages.count(),
'flow_count': flows.count(), 'flow_count': flows.count(),
'fanpages': fanpages, 'fanpages': fanpages,
'recent_logs': recent_logs, 'recent_logs': recent_logs,
'today_messages_count': today_messages_count,
} }
return render(request, 'core/dashboard.html', context) return render(request, 'core/dashboard.html', context)
@ -54,10 +61,11 @@ def webhook(request):
# Handle incoming messages # Handle incoming messages
try: try:
data = json.loads(request.body.decode('utf-8')) data = json.loads(request.body.decode('utf-8'))
# Process the webhook payload here in the future # Process the webhook payload
# For now, just return 200 OK handle_webhook_event(data)
return HttpResponse('EVENT_RECEIVED') return HttpResponse('EVENT_RECEIVED')
except Exception as e: except Exception as e:
# Log the error if necessary
return HttpResponse('Error processing request', status=400) return HttpResponse('Error processing request', status=400)
return HttpResponse('Method not allowed', status=405) return HttpResponse('Method not allowed', status=405)

View File

@ -1,3 +1,4 @@
Django==5.2.7 Django==5.2.7
mysqlclient==2.2.7 mysqlclient==2.2.7
python-dotenv==1.1.1 python-dotenv==1.1.1
requests==2.31.0