ai
This commit is contained in:
parent
7af198c681
commit
bbb51c52f6
BIN
ai/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
ai/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
ai/__pycache__/local_ai_api.cpython-311.pyc
Normal file
BIN
ai/__pycache__/local_ai_api.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,3 +1,11 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from .models import BotSettings, Message
|
||||||
|
|
||||||
# Register your models here.
|
@admin.register(BotSettings)
|
||||||
|
class BotSettingsAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('id', 'is_active', 'system_prompt')
|
||||||
|
|
||||||
|
@admin.register(Message)
|
||||||
|
class MessageAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('sender_number', 'message_in', 'message_out', 'timestamp')
|
||||||
|
list_filter = ('timestamp', 'sender_number')
|
||||||
33
core/migrations/0001_initial.py
Normal file
33
core/migrations/0001_initial.py
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
# Generated by Django 5.2.7 on 2026-02-07 14:17
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='BotSettings',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('system_prompt', models.TextField(default='You are a helpful assistant.')),
|
||||||
|
('is_active', models.BooleanField(default=True)),
|
||||||
|
('verify_token', models.CharField(default='my_secure_token_123', max_length=255)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Message',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('sender_number', models.CharField(max_length=50)),
|
||||||
|
('message_in', models.TextField()),
|
||||||
|
('message_out', models.TextField(blank=True, null=True)),
|
||||||
|
('timestamp', models.DateTimeField(auto_now_add=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
BIN
core/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
BIN
core/migrations/__pycache__/0001_initial.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
@ -1,3 +1,18 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
# Create your models here.
|
class BotSettings(models.Model):
|
||||||
|
system_prompt = models.TextField(default="You are a helpful assistant.")
|
||||||
|
is_active = models.BooleanField(default=True)
|
||||||
|
verify_token = models.CharField(max_length=255, default="my_secure_token_123")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Bot Settings (Active: {self.is_active})"
|
||||||
|
|
||||||
|
class Message(models.Model):
|
||||||
|
sender_number = models.CharField(max_length=50)
|
||||||
|
message_in = models.TextField()
|
||||||
|
message_out = models.TextField(null=True, blank=True)
|
||||||
|
timestamp = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"From {self.sender_number} at {self.timestamp}"
|
||||||
@ -1,25 +1,105 @@
|
|||||||
|
{% load static %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>{% block title %}Knowledge Base{% endblock %}</title>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
{% if project_description %}
|
<title>{% block title %}WhatsApp AI Bot{% endblock %}</title>
|
||||||
<meta name="description" content="{{ project_description }}">
|
<!-- Google Fonts -->
|
||||||
<meta property="og:description" content="{{ project_description }}">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<meta property="twitter:description" content="{{ project_description }}">
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
{% endif %}
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||||
{% if project_image_url %}
|
<!-- Bootstrap 5 CSS -->
|
||||||
<meta property="og:image" content="{{ project_image_url }}">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<meta property="twitter:image" content="{{ project_image_url }}">
|
<!-- Custom Styles -->
|
||||||
{% endif %}
|
<style>
|
||||||
{% load static %}
|
:root {
|
||||||
<link rel="stylesheet" href="{% static 'css/custom.css' %}?v={{ deployment_timestamp }}">
|
--wa-green: #00A884;
|
||||||
{% block head %}{% endblock %}
|
--wa-green-dark: #128C7E;
|
||||||
|
--wa-teal: #075E54;
|
||||||
|
--wa-bg: #F0F2F5;
|
||||||
|
--wa-text: #111B21;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
background-color: var(--wa-bg);
|
||||||
|
color: var(--wa-text);
|
||||||
|
}
|
||||||
|
.navbar {
|
||||||
|
background-color: var(--wa-teal);
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
.navbar-brand {
|
||||||
|
font-weight: 700;
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
.nav-link {
|
||||||
|
color: rgba(255,255,255,0.8) !important;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.nav-link:hover {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
border: none;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
.btn-primary {
|
||||||
|
background-color: var(--wa-green);
|
||||||
|
border-color: var(--wa-green);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.btn-primary:hover {
|
||||||
|
background-color: var(--wa-green-dark);
|
||||||
|
border-color: var(--wa-green-dark);
|
||||||
|
}
|
||||||
|
.status-badge {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
padding: 0.3rem 0.6rem;
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
.status-active {
|
||||||
|
background-color: #dcf8c6;
|
||||||
|
color: #075e54;
|
||||||
|
}
|
||||||
|
.status-inactive {
|
||||||
|
background-color: #f8d7da;
|
||||||
|
color: #721c24;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% block extra_css %}{% endblock %}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
{% block content %}{% endblock %}
|
<nav class="navbar navbar-expand-lg mb-4">
|
||||||
</body>
|
<div class="container">
|
||||||
|
<a class="navbar-brand" href="{% url 'index' %}">WhatsApp AI</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav ms-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{% url 'index' %}">Dashboard</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{% url 'settings' %}">Bot Settings</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/admin/" target="_blank">Admin Panel</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
</html>
|
<div class="container">
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
{% block extra_js %}{% endblock %}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -1,145 +1,65 @@
|
|||||||
{% extends "base.html" %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% block title %}{{ project_name }}{% endblock %}
|
{% block title %}Dashboard - WhatsApp AI Bot{% endblock %}
|
||||||
|
|
||||||
{% block head %}
|
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
|
||||||
<style>
|
|
||||||
:root {
|
|
||||||
--bg-color-start: #6a11cb;
|
|
||||||
--bg-color-end: #2575fc;
|
|
||||||
--text-color: #ffffff;
|
|
||||||
--card-bg-color: rgba(255, 255, 255, 0.01);
|
|
||||||
--card-border-color: rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
|
|
||||||
color: var(--text-color);
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
min-height: 100vh;
|
|
||||||
text-align: center;
|
|
||||||
overflow: hidden;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
body::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'><path d='M-10 10L110 10M10 -10L10 110' stroke-width='1' stroke='rgba(255,255,255,0.05)'/></svg>");
|
|
||||||
animation: bg-pan 20s linear infinite;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes bg-pan {
|
|
||||||
0% {
|
|
||||||
background-position: 0% 0%;
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
background-position: 100% 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
background: var(--card-bg-color);
|
|
||||||
border: 1px solid var(--card-border-color);
|
|
||||||
border-radius: 16px;
|
|
||||||
padding: 2.5rem 2rem;
|
|
||||||
backdrop-filter: blur(20px);
|
|
||||||
-webkit-backdrop-filter: blur(20px);
|
|
||||||
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.25);
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
font-size: clamp(2.2rem, 3vw + 1.2rem, 3.2rem);
|
|
||||||
font-weight: 700;
|
|
||||||
margin: 0 0 1.2rem;
|
|
||||||
letter-spacing: -0.02em;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
opacity: 0.92;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loader {
|
|
||||||
margin: 1.5rem auto;
|
|
||||||
width: 56px;
|
|
||||||
height: 56px;
|
|
||||||
border: 4px solid rgba(255, 255, 255, 0.25);
|
|
||||||
border-top-color: #fff;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.runtime code {
|
|
||||||
background: rgba(0, 0, 0, 0.25);
|
|
||||||
padding: 0.15rem 0.45rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sr-only {
|
|
||||||
position: absolute;
|
|
||||||
width: 1px;
|
|
||||||
height: 1px;
|
|
||||||
padding: 0;
|
|
||||||
margin: -1px;
|
|
||||||
overflow: hidden;
|
|
||||||
clip: rect(0, 0, 0, 0);
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
footer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 1rem;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
opacity: 0.75;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<main>
|
<div class="row mb-4">
|
||||||
<div class="card">
|
<div class="col-md-8">
|
||||||
<h1>Analyzing your requirements and generating your app…</h1>
|
<h2 class="fw-bold">Live Chat Monitor</h2>
|
||||||
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
|
<p class="text-muted">Real-time view of incoming and AI-generated messages.</p>
|
||||||
<span class="sr-only">Loading…</span>
|
|
||||||
</div>
|
</div>
|
||||||
<p class="hint">AppWizzy AI is collecting your requirements and applying the first changes.</p>
|
<div class="col-md-4 text-end">
|
||||||
<p class="hint">This page will refresh automatically as the plan is implemented.</p>
|
<div class="card p-3 d-inline-block">
|
||||||
<p class="runtime">
|
<span class="fw-bold me-2">Bot Status:</span>
|
||||||
Runtime: Django <code>{{ django_version }}</code> · Python <code>{{ python_version }}</code>
|
{% if settings.is_active %}
|
||||||
— UTC <code>{{ current_time|date:"Y-m-d H:i:s" }}</code>
|
<span class="status-badge status-active">Active</span>
|
||||||
</p>
|
{% else %}
|
||||||
</div>
|
<span class="status-badge status-inactive">Paused</span>
|
||||||
</main>
|
{% endif %}
|
||||||
<footer>
|
</div>
|
||||||
Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC)
|
</div>
|
||||||
</footer>
|
</div>
|
||||||
{% endblock %}
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover align-middle mb-0">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th style="width: 150px;">Sender</th>
|
||||||
|
<th>Message In</th>
|
||||||
|
<th>AI Response</th>
|
||||||
|
<th style="width: 180px;">Time</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for msg in messages %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="badge bg-light text-dark p-2">{{ msg.sender_number }}</span>
|
||||||
|
</td>
|
||||||
|
<td>{{ msg.message_in }}</td>
|
||||||
|
<td>
|
||||||
|
{% if msg.message_out %}
|
||||||
|
<div class="p-2 rounded bg-light" style="border-left: 4px solid var(--wa-green);">
|
||||||
|
{{ msg.message_out }}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted italic">Processing...</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<small class="text-muted">{{ msg.timestamp|date:"M d, H:i:s" }}</small>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% empty %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="4" class="text-center py-5 text-muted">
|
||||||
|
No messages received yet. Send a message to your WhatsApp number to see it here.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|||||||
51
core/templates/core/settings.html
Normal file
51
core/templates/core/settings.html
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block title %}Bot Settings - WhatsApp AI Bot{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-8">
|
||||||
|
<div class="card p-4">
|
||||||
|
<h2 class="fw-bold mb-4">AI Bot Personality</h2>
|
||||||
|
<form method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="form-label fw-600">System Instruction (Prompt)</label>
|
||||||
|
<textarea name="system_prompt" class="form-control" rows="6" placeholder="Define how the AI should behave...">{{ settings.system_prompt }}</textarea>
|
||||||
|
<div class="form-text mt-2">
|
||||||
|
This instruction defines the bot's tone, personality, and knowledge limits.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="form-label fw-600">Verification Token (Meta Webhook)</label>
|
||||||
|
<input type="text" name="verify_token" class="form-control" value="{{ settings.verify_token }}">
|
||||||
|
<div class="form-text">
|
||||||
|
Use this token when setting up the webhook in the Meta Developer Portal.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-check form-switch mb-4">
|
||||||
|
<input class="form-check-input" type="checkbox" name="is_active" id="activeSwitch" {% if settings.is_active %}checked{% endif %}>
|
||||||
|
<label class="form-check-label fw-600" for="activeSwitch">Enable Auto-Reply</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between">
|
||||||
|
<a href="{% url 'index' %}" class="btn btn-light">Back to Dashboard</a>
|
||||||
|
<button type="submit" class="btn btn-primary px-4">Save Changes</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card mt-4 p-4 border-info bg-info bg-opacity-10">
|
||||||
|
<h5><i class="bi bi-info-circle me-2"></i>Setup Tip</h5>
|
||||||
|
<p class="mb-0 small text-dark">
|
||||||
|
Your Meta Webhook URL: <code>https://your-domain.com/webhook/whatsapp/</code><br>
|
||||||
|
Verification Token: <code>{{ settings.verify_token }}</code>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@ -1,7 +1,8 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
from . import views
|
||||||
from .views import home
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", home, name="home"),
|
path('', views.index, name='index'),
|
||||||
]
|
path('settings/', views.settings_view, name='settings'),
|
||||||
|
path('webhook/whatsapp/', views.webhook, name='whatsapp_webhook'),
|
||||||
|
]
|
||||||
130
core/views.py
130
core/views.py
@ -1,25 +1,113 @@
|
|||||||
import os
|
import json
|
||||||
import platform
|
import logging
|
||||||
|
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
|
||||||
|
|
||||||
from django import get_version as django_version
|
logger = logging.getLogger(__name__)
|
||||||
from django.shortcuts import render
|
|
||||||
from django.utils import timezone
|
|
||||||
|
|
||||||
|
|
||||||
def home(request):
|
|
||||||
"""Render the landing screen with loader and environment details."""
|
|
||||||
host_name = request.get_host().lower()
|
|
||||||
agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic"
|
|
||||||
now = timezone.now()
|
|
||||||
|
|
||||||
|
def index(request):
|
||||||
|
messages = Message.objects.all().order_by('-timestamp')[:50]
|
||||||
|
settings = BotSettings.objects.first()
|
||||||
|
if not settings:
|
||||||
|
settings = BotSettings.objects.create()
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"project_name": "New Style",
|
'messages': messages,
|
||||||
"agent_brand": agent_brand,
|
'settings': settings,
|
||||||
"django_version": django_version(),
|
|
||||||
"python_version": platform.python_version(),
|
|
||||||
"current_time": now,
|
|
||||||
"host_name": host_name,
|
|
||||||
"project_description": os.getenv("PROJECT_DESCRIPTION", ""),
|
|
||||||
"project_image_url": os.getenv("PROJECT_IMAGE_URL", ""),
|
|
||||||
}
|
}
|
||||||
return render(request, "core/index.html", context)
|
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.save()
|
||||||
|
return redirect('index')
|
||||||
|
|
||||||
|
return render(request, 'core/settings.html', {'settings': settings})
|
||||||
|
|
||||||
|
@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
|
||||||
|
if 'object' in data and data['object'] == 'whatsapp_business_account':
|
||||||
|
for entry in data['entry']:
|
||||||
|
for change in entry['changes']:
|
||||||
|
value = change['value']
|
||||||
|
if 'messages' in value:
|
||||||
|
for msg in value['messages']:
|
||||||
|
sender_number = msg['from']
|
||||||
|
message_body = msg.get('text', {}).get('body', '')
|
||||||
|
|
||||||
|
if message_body:
|
||||||
|
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", # Explicitly request gemini-1.5-flash
|
||||||
|
})
|
||||||
|
|
||||||
|
if response.get("success"):
|
||||||
|
ai_text = LocalAIApi.extract_text(response)
|
||||||
|
db_msg.message_out = ai_text
|
||||||
|
db_msg.save()
|
||||||
|
|
||||||
|
# NOTE: In a real app, you'd call Meta API here to send db_msg.message_out back to sender_number.
|
||||||
|
# For this task, we focus on the integration and dashboard as requested.
|
||||||
|
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)}")
|
||||||
Loading…
x
Reference in New Issue
Block a user