Spaces:
Running
Running
connect to supabase
Browse files- =3.0.0 +0 -0
- Dockerfile +15 -14
- README.md +0 -13
- __pycache__/ai_service.cpython-313.pyc +0 -0
- __pycache__/app.cpython-313.pyc +0 -0
- __pycache__/config.cpython-313.pyc +0 -0
- __pycache__/database.cpython-313.pyc +0 -0
- __pycache__/main.cpython-313.pyc +0 -0
- __pycache__/telegram_handlers.cpython-313.pyc +0 -0
- __pycache__/utils.cpython-313.pyc +0 -0
- ai_service.py +69 -0
- app.py +0 -113
- config.py +40 -0
- database.py +202 -0
- database_schema.sql +56 -0
- main.py +25 -0
- requirements.txt +1 -0
- rest.http +21 -0
- supabase_schema.sql +90 -0
- telegram_handlers.py +64 -0
- utils.py +16 -0
=3.0.0
ADDED
|
File without changes
|
Dockerfile
CHANGED
|
@@ -4,14 +4,17 @@ FROM python:3.11-slim
|
|
| 4 |
# Set working directory
|
| 5 |
WORKDIR /app
|
| 6 |
|
| 7 |
-
# Set environment variables
|
| 8 |
ENV PYTHONDONTWRITEBYTECODE=1 \
|
| 9 |
PYTHONUNBUFFERED=1 \
|
| 10 |
-
PORT=
|
|
|
|
|
|
|
| 11 |
|
| 12 |
-
# Install system dependencies
|
| 13 |
RUN apt-get update && apt-get install -y \
|
| 14 |
gcc \
|
|
|
|
| 15 |
&& rm -rf /var/lib/apt/lists/*
|
| 16 |
|
| 17 |
# Copy requirements first for better caching
|
|
@@ -21,20 +24,18 @@ COPY requirements.txt .
|
|
| 21 |
RUN pip install --no-cache-dir --upgrade pip && \
|
| 22 |
pip install --no-cache-dir -r requirements.txt
|
| 23 |
|
| 24 |
-
# Copy application code
|
| 25 |
-
COPY
|
| 26 |
|
| 27 |
-
# Create
|
| 28 |
-
RUN
|
| 29 |
-
chown -R app:app /app
|
| 30 |
-
USER app
|
| 31 |
|
| 32 |
-
# Expose port
|
| 33 |
-
EXPOSE
|
| 34 |
|
| 35 |
# Health check
|
| 36 |
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
|
| 37 |
-
CMD curl -f http://localhost:
|
| 38 |
|
| 39 |
-
# Run the application
|
| 40 |
-
CMD ["uvicorn", "
|
|
|
|
| 4 |
# Set working directory
|
| 5 |
WORKDIR /app
|
| 6 |
|
| 7 |
+
# Set environment variables for Hugging Face Spaces (can be overridden for local development)
|
| 8 |
ENV PYTHONDONTWRITEBYTECODE=1 \
|
| 9 |
PYTHONUNBUFFERED=1 \
|
| 10 |
+
PORT=8000 \
|
| 11 |
+
HF_HOME=/app/cache \
|
| 12 |
+
TRANSFORMERS_CACHE=/app/cache
|
| 13 |
|
| 14 |
+
# Install system dependencies including curl for health check
|
| 15 |
RUN apt-get update && apt-get install -y \
|
| 16 |
gcc \
|
| 17 |
+
curl \
|
| 18 |
&& rm -rf /var/lib/apt/lists/*
|
| 19 |
|
| 20 |
# Copy requirements first for better caching
|
|
|
|
| 24 |
RUN pip install --no-cache-dir --upgrade pip && \
|
| 25 |
pip install --no-cache-dir -r requirements.txt
|
| 26 |
|
| 27 |
+
# Copy all application code
|
| 28 |
+
COPY . .
|
| 29 |
|
| 30 |
+
# Create cache directory for Hugging Face models
|
| 31 |
+
RUN mkdir -p /app/cache && chmod 777 /app/cache
|
|
|
|
|
|
|
| 32 |
|
| 33 |
+
# Expose port (default 8000 for local, can be set to 7860 for Hugging Face Spaces)
|
| 34 |
+
EXPOSE ${PORT}
|
| 35 |
|
| 36 |
# Health check
|
| 37 |
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
|
| 38 |
+
CMD curl -f http://localhost:${PORT}/ || exit 1
|
| 39 |
|
| 40 |
+
# Run the application (uses PORT environment variable)
|
| 41 |
+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "${PORT}"]
|
README.md
CHANGED
|
@@ -1,14 +1 @@
|
|
| 1 |
-
---
|
| 2 |
-
title: customer_service
|
| 3 |
-
sdk: docker
|
| 4 |
-
emoji: 🚀
|
| 5 |
-
colorFrom: blue
|
| 6 |
-
colorTo: blue
|
| 7 |
-
app_file: app.py
|
| 8 |
-
---
|
| 9 |
# customer_service
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
---
|
| 13 |
-
|
| 14 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
# customer_service
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
__pycache__/ai_service.cpython-313.pyc
ADDED
|
Binary file (3.15 kB). View file
|
|
|
__pycache__/app.cpython-313.pyc
ADDED
|
Binary file (3.29 kB). View file
|
|
|
__pycache__/config.cpython-313.pyc
ADDED
|
Binary file (1.93 kB). View file
|
|
|
__pycache__/database.cpython-313.pyc
ADDED
|
Binary file (10.2 kB). View file
|
|
|
__pycache__/main.cpython-313.pyc
ADDED
|
Binary file (1.6 kB). View file
|
|
|
__pycache__/telegram_handlers.cpython-313.pyc
ADDED
|
Binary file (2.74 kB). View file
|
|
|
__pycache__/utils.cpython-313.pyc
ADDED
|
Binary file (1.08 kB). View file
|
|
|
ai_service.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import re
|
| 2 |
+
from config import pc, index, groq_client, EMBED_MODEL, GROQ_MODEL, PROMPT
|
| 3 |
+
from database import db_manager
|
| 4 |
+
|
| 5 |
+
def clean_ai_response(text: str):
|
| 6 |
+
cleaned_text = re.sub(r'<think>.*?</think>', '', text, flags=re.DOTALL)
|
| 7 |
+
return cleaned_text.strip()
|
| 8 |
+
|
| 9 |
+
async def get_ai_response(user_query: str, telegram_id: int = None):
|
| 10 |
+
|
| 11 |
+
if not pc or not index or not groq_client:
|
| 12 |
+
return "Ai service is not available at the moment. Please try again later."
|
| 13 |
+
|
| 14 |
+
# Save user message if database is available and telegram_id is provided
|
| 15 |
+
if telegram_id and db_manager:
|
| 16 |
+
db_manager.save_message(telegram_id, user_query, "user")
|
| 17 |
+
|
| 18 |
+
conversation_history = ""
|
| 19 |
+
if telegram_id and db_manager:
|
| 20 |
+
conversation_history = db_manager.get_formatted_history(telegram_id, limit=6)
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
query_embedding = pc.inference.embed(
|
| 24 |
+
model=EMBED_MODEL,
|
| 25 |
+
inputs=[user_query],
|
| 26 |
+
parameters={"input_type": "query"}
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
# Search Pinecone for Bank Context
|
| 30 |
+
search_results = index.query(
|
| 31 |
+
vector=query_embedding[0].values,
|
| 32 |
+
top_k=3,
|
| 33 |
+
include_metadata=True
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
retrieved_context = "\n".join([res.metadata['original_text'] for res in search_results.matches])
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
user_content = f"""
|
| 40 |
+
### Historical Conversation:
|
| 41 |
+
{conversation_history}
|
| 42 |
+
|
| 43 |
+
### Retrieved Context from Bank Documents:
|
| 44 |
+
{retrieved_context}
|
| 45 |
+
|
| 46 |
+
### Current User Message:
|
| 47 |
+
{user_query}
|
| 48 |
+
|
| 49 |
+
بناءً على ما سبق، قدم إجابة دقيقة ومفيدة للعميل:
|
| 50 |
+
"""
|
| 51 |
+
completion = groq_client.chat.completions.create(
|
| 52 |
+
messages=[
|
| 53 |
+
{"role": "system", "content": PROMPT},
|
| 54 |
+
{"role": "user", "content": f"{conversation_history}\n\nRetrieved Context: {retrieved_context}\n\nCurrent User Message: {user_query}"}
|
| 55 |
+
],
|
| 56 |
+
model=GROQ_MODEL,
|
| 57 |
+
temperature=0.1,
|
| 58 |
+
max_completion_tokens=800,
|
| 59 |
+
top_p=0.9,
|
| 60 |
+
)
|
| 61 |
+
ai_response = completion.choices[0].message.content
|
| 62 |
+
cleaned_response = clean_ai_response(ai_response)
|
| 63 |
+
|
| 64 |
+
# Save assistant response if database is available and telegram_id is provided
|
| 65 |
+
if telegram_id and db_manager:
|
| 66 |
+
db_manager.save_message(telegram_id, cleaned_response, "assistant")
|
| 67 |
+
|
| 68 |
+
return cleaned_response
|
| 69 |
+
|
app.py
DELETED
|
@@ -1,113 +0,0 @@
|
|
| 1 |
-
import os
|
| 2 |
-
from fastapi import FastAPI, Request
|
| 3 |
-
from pinecone import Pinecone
|
| 4 |
-
from groq import Groq
|
| 5 |
-
import httpx
|
| 6 |
-
import re
|
| 7 |
-
|
| 8 |
-
# 1. Configuration & Clients
|
| 9 |
-
# Use Hugging Face Secrets for these!
|
| 10 |
-
PINECONE_API_KEY = os.environ.get("PINECONE_API_KEY")
|
| 11 |
-
GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
|
| 12 |
-
TELEGRAM_TOKEN = os.environ.get("TELEGRAM_TOKEN")
|
| 13 |
-
TELEGRAM_URL = f"https://149.154.167.220/bot{TELEGRAM_TOKEN}/sendMessage"
|
| 14 |
-
|
| 15 |
-
EMBED_MODEL= os.environ.get("EMBED_MODEL")
|
| 16 |
-
GROQ_MODEL = os.environ.get("GROQ_MODEL")
|
| 17 |
-
PROMPT = os.environ.get("PROMPT")
|
| 18 |
-
|
| 19 |
-
pc = Pinecone(api_key=PINECONE_API_KEY)
|
| 20 |
-
index = pc.Index("customerserviceindex")
|
| 21 |
-
groq_client = Groq(api_key=GROQ_API_KEY)
|
| 22 |
-
|
| 23 |
-
app = FastAPI()
|
| 24 |
-
|
| 25 |
-
def clean_ai_response(text: str):
|
| 26 |
-
# إزالة كل ما بين وسمي <think> و </think> بما في ذلك الوسوم نفسها
|
| 27 |
-
cleaned_text = re.sub(r'<think>.*?</think>', '', text, flags=re.DOTALL)
|
| 28 |
-
return cleaned_text.strip()
|
| 29 |
-
|
| 30 |
-
# 2. The Core AI Logic
|
| 31 |
-
async def get_ai_response(user_query: str):
|
| 32 |
-
# Vectorize query using Pinecone Inference
|
| 33 |
-
query_embedding = pc.inference.embed(
|
| 34 |
-
model=EMBED_MODEL,
|
| 35 |
-
inputs=[user_query],
|
| 36 |
-
parameters={"input_type": "query"}
|
| 37 |
-
)
|
| 38 |
-
|
| 39 |
-
# Search Pinecone for Bank Context
|
| 40 |
-
search_results = index.query(
|
| 41 |
-
vector=query_embedding[0].values,
|
| 42 |
-
top_k=3,
|
| 43 |
-
include_metadata=True
|
| 44 |
-
)
|
| 45 |
-
|
| 46 |
-
retrieved_context = "\n".join([res.metadata['original_text'] for res in search_results.matches])
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
prompt = f"""
|
| 50 |
-
{PROMPT}
|
| 51 |
-
|
| 52 |
-
Message:{user_query}
|
| 53 |
-
|
| 54 |
-
Retrieved Context:{retrieved_context}
|
| 55 |
-
|
| 56 |
-
Final Answer:
|
| 57 |
-
"""
|
| 58 |
-
|
| 59 |
-
completion = groq_client.chat.completions.create(
|
| 60 |
-
messages=[{"role": "user", "content": prompt}],
|
| 61 |
-
model=GROQ_MODEL,
|
| 62 |
-
temperature=0.1,
|
| 63 |
-
max_completion_tokens=800,
|
| 64 |
-
top_p=0.9,
|
| 65 |
-
)
|
| 66 |
-
ai_response = completion.choices[0].message.content
|
| 67 |
-
return clean_ai_response(ai_response)
|
| 68 |
-
|
| 69 |
-
# 3. The Webhook Endpoint
|
| 70 |
-
@app.post("/webhook")
|
| 71 |
-
async def telegram_webhook(request: Request):
|
| 72 |
-
data = await request.json()
|
| 73 |
-
|
| 74 |
-
if "message" in data:
|
| 75 |
-
chat_id = data["message"]["chat"]["id"]
|
| 76 |
-
user_text = data["message"].get("text", "")
|
| 77 |
-
|
| 78 |
-
if user_text:
|
| 79 |
-
# Get the intelligent response
|
| 80 |
-
ai_answer = await get_ai_response(user_text)
|
| 81 |
-
# Send back to Telegram
|
| 82 |
-
async with httpx.AsyncClient(verify=False) as client:
|
| 83 |
-
await client.post(
|
| 84 |
-
TELEGRAM_URL,
|
| 85 |
-
headers={"Host": "api.telegram.org"},
|
| 86 |
-
json={
|
| 87 |
-
"chat_id": chat_id,
|
| 88 |
-
"text": ai_answer,
|
| 89 |
-
"parse_mode": "Markdown"
|
| 90 |
-
}
|
| 91 |
-
)
|
| 92 |
-
|
| 93 |
-
return {"status": "ok"}
|
| 94 |
-
|
| 95 |
-
@app.get("/")
|
| 96 |
-
async def root():
|
| 97 |
-
return {"message": "Hadhramout Bank AI Backend is Live"}
|
| 98 |
-
|
| 99 |
-
@app.post("/test")
|
| 100 |
-
async def test_webhook(request: Request):
|
| 101 |
-
data = await request.json()
|
| 102 |
-
response = await get_ai_response(data["message"]["text"])
|
| 103 |
-
return {"response": response}
|
| 104 |
-
|
| 105 |
-
import socket
|
| 106 |
-
|
| 107 |
-
@app.get("/dns-test")
|
| 108 |
-
async def dns_test():
|
| 109 |
-
try:
|
| 110 |
-
ip = socket.gethostbyname("api.telegram.org")
|
| 111 |
-
return {"resolved_ip": ip}
|
| 112 |
-
except Exception as e:
|
| 113 |
-
return {"error": str(e)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
config.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from pinecone import Pinecone
|
| 3 |
+
from groq import Groq
|
| 4 |
+
from dotenv import load_dotenv
|
| 5 |
+
|
| 6 |
+
# Load environment variables from .env file
|
| 7 |
+
load_dotenv()
|
| 8 |
+
|
| 9 |
+
# Environment Variables
|
| 10 |
+
PINECONE_API_KEY = os.environ.get("PINECONE_API_KEY")
|
| 11 |
+
GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
|
| 12 |
+
TELEGRAM_TOKEN = os.environ.get("TELEGRAM_TOKEN")
|
| 13 |
+
SUPABASE_URL = os.environ.get("SUPABASE_URL")
|
| 14 |
+
SUPABASE_KEY = os.environ.get("SUPABASE_KEY")
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
# Only create TELEGRAM_URL if token exists
|
| 18 |
+
TELEGRAM_URL = f"https://149.154.167.220/bot{TELEGRAM_TOKEN}/sendMessage" if TELEGRAM_TOKEN else None
|
| 19 |
+
|
| 20 |
+
EMBED_MODEL = os.environ.get("EMBED_MODEL", "multilingual-e5-large")
|
| 21 |
+
GROQ_MODEL = os.environ.get("GROQ_MODEL", "llama-3.1-8b-instant")
|
| 22 |
+
PROMPT = os.environ.get("PROMPT", "You are a helpful customer service assistant for Hadhramout Bank. Answer the user's question based on the provided context. If the context doesn't contain the answer, politely say you don't have enough information to help with that specific query.")
|
| 23 |
+
|
| 24 |
+
# Initialize clients only if API keys are available
|
| 25 |
+
pc = None
|
| 26 |
+
if PINECONE_API_KEY:
|
| 27 |
+
pc = Pinecone(api_key=PINECONE_API_KEY)
|
| 28 |
+
|
| 29 |
+
groq_client = None
|
| 30 |
+
if GROQ_API_KEY:
|
| 31 |
+
try:
|
| 32 |
+
groq_client = Groq(api_key=GROQ_API_KEY)
|
| 33 |
+
except Exception as e:
|
| 34 |
+
print(f"Warning: Failed to initialize Groq client: {e}")
|
| 35 |
+
groq_client = None
|
| 36 |
+
|
| 37 |
+
# Initialize index only if Pinecone client is available
|
| 38 |
+
index = None
|
| 39 |
+
if pc:
|
| 40 |
+
index = pc.Index("customerserviceindex")
|
database.py
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from datetime import datetime
|
| 3 |
+
from typing import List, Dict, Optional
|
| 4 |
+
from supabase import create_client, Client
|
| 5 |
+
import logging
|
| 6 |
+
from config import SUPABASE_URL, SUPABASE_KEY
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class DatabaseManager:
|
| 10 |
+
def __init__(self, supabase_url: str = SUPABASE_URL, supabase_key: str = SUPABASE_KEY):
|
| 11 |
+
if not supabase_url or not supabase_key:
|
| 12 |
+
raise ValueError("SUPABASE_URL and SUPABASE_KEY must be set in environment variables")
|
| 13 |
+
|
| 14 |
+
self.supabase: Client = create_client(supabase_url, supabase_key)
|
| 15 |
+
self.logger = logging.getLogger(__name__)
|
| 16 |
+
|
| 17 |
+
def create_or_update_user(self, telegram_id: int, username: str = None,
|
| 18 |
+
first_name: str = None, last_name: str = None):
|
| 19 |
+
"""Create or update user information"""
|
| 20 |
+
try:
|
| 21 |
+
# Check if user exists
|
| 22 |
+
existing_user = self.supabase.table("users").select("id").eq("telegram_id", telegram_id).execute()
|
| 23 |
+
|
| 24 |
+
user_data = {
|
| 25 |
+
"telegram_id": telegram_id,
|
| 26 |
+
"username": username,
|
| 27 |
+
"first_name": first_name,
|
| 28 |
+
"last_name": last_name,
|
| 29 |
+
"updated_at": datetime.utcnow().isoformat()
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
if existing_user.data:
|
| 33 |
+
# Update existing user
|
| 34 |
+
result = self.supabase.table("users").update(user_data).eq("telegram_id", telegram_id).execute()
|
| 35 |
+
else:
|
| 36 |
+
# Create new user
|
| 37 |
+
user_data["created_at"] = datetime.utcnow().isoformat()
|
| 38 |
+
result = self.supabase.table("users").insert(user_data).execute()
|
| 39 |
+
|
| 40 |
+
return result.data[0] if result.data else None
|
| 41 |
+
|
| 42 |
+
except Exception as e:
|
| 43 |
+
self.logger.error(f"Error creating/updating user: {e}")
|
| 44 |
+
return None
|
| 45 |
+
|
| 46 |
+
def save_message(self, telegram_id: int, message_text: str, message_type: str):
|
| 47 |
+
"""Save a message to the database"""
|
| 48 |
+
try:
|
| 49 |
+
# Ensure user exists
|
| 50 |
+
self.create_or_update_user(telegram_id)
|
| 51 |
+
|
| 52 |
+
# Save message
|
| 53 |
+
message_data = {
|
| 54 |
+
"telegram_id": telegram_id,
|
| 55 |
+
"message_text": message_text,
|
| 56 |
+
"message_type": message_type,
|
| 57 |
+
"created_at": datetime.utcnow().isoformat()
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
result = self.supabase.table("messages").insert(message_data).execute()
|
| 61 |
+
|
| 62 |
+
# Ensure active session exists
|
| 63 |
+
self._ensure_active_session(telegram_id)
|
| 64 |
+
|
| 65 |
+
return result.data[0] if result.data else None
|
| 66 |
+
|
| 67 |
+
except Exception as e:
|
| 68 |
+
self.logger.error(f"Error saving message: {e}")
|
| 69 |
+
return None
|
| 70 |
+
|
| 71 |
+
def get_conversation_history(self, telegram_id: int, limit: int = 10) -> List[Dict]:
|
| 72 |
+
"""Get conversation history for a user"""
|
| 73 |
+
try:
|
| 74 |
+
result = (self.supabase.table("messages")
|
| 75 |
+
.select("message_text, message_type, created_at")
|
| 76 |
+
.eq("telegram_id", telegram_id)
|
| 77 |
+
.order("created_at", desc=True)
|
| 78 |
+
.limit(limit)
|
| 79 |
+
.execute())
|
| 80 |
+
|
| 81 |
+
return result.data if result.data else []
|
| 82 |
+
|
| 83 |
+
except Exception as e:
|
| 84 |
+
self.logger.error(f"Error getting conversation history: {e}")
|
| 85 |
+
return []
|
| 86 |
+
|
| 87 |
+
def get_formatted_history(self, telegram_id: int, limit: int = 10) -> str:
|
| 88 |
+
"""Get formatted conversation history for Groq"""
|
| 89 |
+
history = self.get_conversation_history(telegram_id, limit)
|
| 90 |
+
|
| 91 |
+
if not history:
|
| 92 |
+
return ""
|
| 93 |
+
|
| 94 |
+
# Reverse to get chronological order
|
| 95 |
+
history.reverse()
|
| 96 |
+
|
| 97 |
+
formatted_history = "Previous conversation:\n"
|
| 98 |
+
for msg in history:
|
| 99 |
+
role = "User" if msg['message_type'] == 'user' else "Assistant"
|
| 100 |
+
formatted_history += f"{role}: {msg['message_text']}\n"
|
| 101 |
+
|
| 102 |
+
return formatted_history
|
| 103 |
+
|
| 104 |
+
def _ensure_active_session(self, telegram_id: int):
|
| 105 |
+
"""Ensure an active session exists for the user"""
|
| 106 |
+
try:
|
| 107 |
+
# Check for active session
|
| 108 |
+
active_session = (self.supabase.table("conversation_sessions")
|
| 109 |
+
.select("id")
|
| 110 |
+
.eq("telegram_id", telegram_id)
|
| 111 |
+
.is_("session_end", "null")
|
| 112 |
+
.execute())
|
| 113 |
+
|
| 114 |
+
if not active_session.data:
|
| 115 |
+
# Create new session
|
| 116 |
+
session_data = {
|
| 117 |
+
"telegram_id": telegram_id,
|
| 118 |
+
"session_start": datetime.utcnow().isoformat(),
|
| 119 |
+
"created_at": datetime.utcnow().isoformat()
|
| 120 |
+
}
|
| 121 |
+
self.supabase.table("conversation_sessions").insert(session_data).execute()
|
| 122 |
+
|
| 123 |
+
except Exception as e:
|
| 124 |
+
self.logger.error(f"Error ensuring active session: {e}")
|
| 125 |
+
|
| 126 |
+
def start_new_session(self, telegram_id: int):
|
| 127 |
+
"""Start a new conversation session"""
|
| 128 |
+
try:
|
| 129 |
+
# End previous sessions
|
| 130 |
+
self.supabase.table("conversation_sessions").update({
|
| 131 |
+
"session_end": datetime.utcnow().isoformat()
|
| 132 |
+
}).eq("telegram_id", telegram_id).is_("session_end", "null").execute()
|
| 133 |
+
|
| 134 |
+
# Start new session
|
| 135 |
+
session_data = {
|
| 136 |
+
"telegram_id": telegram_id,
|
| 137 |
+
"session_start": datetime.utcnow().isoformat(),
|
| 138 |
+
"created_at": datetime.utcnow().isoformat()
|
| 139 |
+
}
|
| 140 |
+
result = self.supabase.table("conversation_sessions").insert(session_data).execute()
|
| 141 |
+
|
| 142 |
+
return result.data[0] if result.data else None
|
| 143 |
+
|
| 144 |
+
except Exception as e:
|
| 145 |
+
self.logger.error(f"Error starting new session: {e}")
|
| 146 |
+
return None
|
| 147 |
+
|
| 148 |
+
def get_user_stats(self, telegram_id: int) -> Dict:
|
| 149 |
+
"""Get user conversation statistics"""
|
| 150 |
+
try:
|
| 151 |
+
# Get message counts
|
| 152 |
+
message_stats = (self.supabase.table("messages")
|
| 153 |
+
.select("message_type")
|
| 154 |
+
.eq("telegram_id", telegram_id)
|
| 155 |
+
.execute())
|
| 156 |
+
|
| 157 |
+
if not message_stats.data:
|
| 158 |
+
return {
|
| 159 |
+
"total_messages": 0,
|
| 160 |
+
"user_messages": 0,
|
| 161 |
+
"assistant_messages": 0,
|
| 162 |
+
"first_message": None,
|
| 163 |
+
"last_message": None
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
total_messages = len(message_stats.data)
|
| 167 |
+
user_messages = len([m for m in message_stats.data if m['message_type'] == 'user'])
|
| 168 |
+
assistant_messages = len([m for m in message_stats.data if m['message_type'] == 'assistant'])
|
| 169 |
+
|
| 170 |
+
# Get first and last message timestamps
|
| 171 |
+
timestamps = [m['created_at'] for m in message_stats.data]
|
| 172 |
+
first_message = min(timestamps) if timestamps else None
|
| 173 |
+
last_message = max(timestamps) if timestamps else None
|
| 174 |
+
|
| 175 |
+
return {
|
| 176 |
+
"total_messages": total_messages,
|
| 177 |
+
"user_messages": user_messages,
|
| 178 |
+
"assistant_messages": assistant_messages,
|
| 179 |
+
"first_message": first_message,
|
| 180 |
+
"last_message": last_message
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
except Exception as e:
|
| 184 |
+
self.logger.error(f"Error getting user stats: {e}")
|
| 185 |
+
return {
|
| 186 |
+
"total_messages": 0,
|
| 187 |
+
"user_messages": 0,
|
| 188 |
+
"assistant_messages": 0,
|
| 189 |
+
"first_message": None,
|
| 190 |
+
"last_message": None
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
# Global database instance
|
| 194 |
+
try:
|
| 195 |
+
db_manager = DatabaseManager()
|
| 196 |
+
except ValueError as e:
|
| 197 |
+
print(f"Database initialization failed: {e}")
|
| 198 |
+
print("Please set SUPABASE_URL and SUPABASE_KEY environment variables")
|
| 199 |
+
db_manager = None
|
| 200 |
+
except Exception as e:
|
| 201 |
+
print(f"Unexpected database error: {e}")
|
| 202 |
+
db_manager = None
|
database_schema.sql
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- Conversation History Database Schema
|
| 2 |
+
-- SQLite Database Structure
|
| 3 |
+
|
| 4 |
+
-- Users table to store user information
|
| 5 |
+
CREATE TABLE IF NOT EXISTS users (
|
| 6 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 7 |
+
chat_id BIGINT UNIQUE NOT NULL,
|
| 8 |
+
username VARCHAR(255),
|
| 9 |
+
first_name VARCHAR(255),
|
| 10 |
+
last_name VARCHAR(255),
|
| 11 |
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
| 12 |
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
| 13 |
+
);
|
| 14 |
+
|
| 15 |
+
-- Messages table to store conversation history
|
| 16 |
+
CREATE TABLE IF NOT EXISTS messages (
|
| 17 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 18 |
+
chat_id BIGINT NOT NULL,
|
| 19 |
+
message_text TEXT NOT NULL,
|
| 20 |
+
message_type VARCHAR(20) NOT NULL CHECK (message_type IN ('user', 'assistant')),
|
| 21 |
+
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
| 22 |
+
FOREIGN KEY (chat_id) REFERENCES users(chat_id) ON DELETE CASCADE
|
| 23 |
+
);
|
| 24 |
+
|
| 25 |
+
-- Conversation sessions to group messages by session
|
| 26 |
+
CREATE TABLE IF NOT EXISTS conversation_sessions (
|
| 27 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 28 |
+
chat_id BIGINT NOT NULL,
|
| 29 |
+
session_start TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
| 30 |
+
session_end TIMESTAMP NULL,
|
| 31 |
+
message_count INTEGER DEFAULT 0,
|
| 32 |
+
FOREIGN KEY (chat_id) REFERENCES users(chat_id) ON DELETE CASCADE
|
| 33 |
+
);
|
| 34 |
+
|
| 35 |
+
-- Indexes for better performance
|
| 36 |
+
CREATE INDEX IF NOT EXISTS idx_messages_chat_id_timestamp ON messages(chat_id, timestamp);
|
| 37 |
+
CREATE INDEX IF NOT EXISTS idx_users_chat_id ON users(chat_id);
|
| 38 |
+
CREATE INDEX IF NOT EXISTS idx_sessions_chat_id ON conversation_sessions(chat_id);
|
| 39 |
+
|
| 40 |
+
-- Trigger to update user's updated_at timestamp
|
| 41 |
+
CREATE TRIGGER IF NOT EXISTS update_user_timestamp
|
| 42 |
+
AFTER INSERT ON messages
|
| 43 |
+
FOR EACH ROW
|
| 44 |
+
BEGIN
|
| 45 |
+
UPDATE users SET updated_at = CURRENT_TIMESTAMP WHERE chat_id = NEW.chat_id;
|
| 46 |
+
END;
|
| 47 |
+
|
| 48 |
+
-- Trigger to update session message count
|
| 49 |
+
CREATE TRIGGER IF NOT EXISTS update_session_count
|
| 50 |
+
AFTER INSERT ON messages
|
| 51 |
+
FOR EACH ROW
|
| 52 |
+
BEGIN
|
| 53 |
+
UPDATE conversation_sessions
|
| 54 |
+
SET message_count = message_count + 1
|
| 55 |
+
WHERE chat_id = NEW.chat_id AND session_end IS NULL;
|
| 56 |
+
END;
|
main.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI
|
| 2 |
+
from telegram_handlers import telegram_webhook, test_webhook
|
| 3 |
+
from utils import dns_test, test_ai_response
|
| 4 |
+
|
| 5 |
+
app = FastAPI()
|
| 6 |
+
|
| 7 |
+
@app.get("/")
|
| 8 |
+
async def root():
|
| 9 |
+
return {"message": "Hadhramout Bank AI Backend is Live"}
|
| 10 |
+
|
| 11 |
+
@app.post("/webhook")
|
| 12 |
+
async def webhook(request):
|
| 13 |
+
return await telegram_webhook(request)
|
| 14 |
+
|
| 15 |
+
@app.post("/test")
|
| 16 |
+
async def test(request):
|
| 17 |
+
return await test_webhook(request)
|
| 18 |
+
|
| 19 |
+
@app.get("/dns-test")
|
| 20 |
+
async def dns():
|
| 21 |
+
return await dns_test()
|
| 22 |
+
|
| 23 |
+
@app.get("/ai-test")
|
| 24 |
+
async def ai():
|
| 25 |
+
return await test_ai_response()
|
requirements.txt
CHANGED
|
@@ -4,3 +4,4 @@ pinecone
|
|
| 4 |
groq
|
| 5 |
httpx
|
| 6 |
python-dotenv
|
|
|
|
|
|
| 4 |
groq
|
| 5 |
httpx
|
| 6 |
python-dotenv
|
| 7 |
+
supabase
|
rest.http
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
POST https://codeboker-customer-service.hf.space/ai-test HTTP/1.1
|
| 2 |
+
Content-Type: application/json
|
| 3 |
+
|
| 4 |
+
{
|
| 5 |
+
"message": {
|
| 6 |
+
"chat": {"id": 123},
|
| 7 |
+
"text": "هاي"
|
| 8 |
+
}
|
| 9 |
+
}
|
| 10 |
+
###
|
| 11 |
+
GET https://codeboker-customer-service.hf.space/ai-test HTTP/1.1
|
| 12 |
+
|
| 13 |
+
###
|
| 14 |
+
POST https://codeboker-customer-service.hf.space/webhook HTTP/1.1
|
| 15 |
+
Content-Type: application/json
|
| 16 |
+
{
|
| 17 |
+
"message": {
|
| 18 |
+
"chat": {"id": 123},
|
| 19 |
+
"text": "السلام عليكم وؤحمة الله اريد ان استعلم عنلاالخدمات الالكترونية"
|
| 20 |
+
}
|
| 21 |
+
}
|
supabase_schema.sql
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- Supabase Database Schema for Conversation History
|
| 2 |
+
-- Run this in your Supabase SQL Editor
|
| 3 |
+
|
| 4 |
+
-- Enable UUID extension
|
| 5 |
+
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
| 6 |
+
|
| 7 |
+
-- Users table to store user information
|
| 8 |
+
CREATE TABLE IF NOT EXISTS users (
|
| 9 |
+
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
|
| 10 |
+
telegram_id BIGINT UNIQUE NOT NULL,
|
| 11 |
+
username VARCHAR(255),
|
| 12 |
+
first_name VARCHAR(255),
|
| 13 |
+
last_name VARCHAR(255),
|
| 14 |
+
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
| 15 |
+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
| 16 |
+
);
|
| 17 |
+
|
| 18 |
+
-- Messages table to store conversation history
|
| 19 |
+
CREATE TABLE IF NOT EXISTS messages (
|
| 20 |
+
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
|
| 21 |
+
telegram_id BIGINT NOT NULL,
|
| 22 |
+
message_text TEXT NOT NULL,
|
| 23 |
+
message_type VARCHAR(20) NOT NULL CHECK (message_type IN ('user', 'assistant')),
|
| 24 |
+
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
| 25 |
+
FOREIGN KEY (telegram_id) REFERENCES users(telegram_id) ON DELETE CASCADE
|
| 26 |
+
);
|
| 27 |
+
|
| 28 |
+
-- Conversation sessions to group messages by session
|
| 29 |
+
CREATE TABLE IF NOT EXISTS conversation_sessions (
|
| 30 |
+
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
|
| 31 |
+
telegram_id BIGINT NOT NULL,
|
| 32 |
+
session_start TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
| 33 |
+
session_end TIMESTAMP WITH TIME ZONE,
|
| 34 |
+
message_count INTEGER DEFAULT 0,
|
| 35 |
+
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
| 36 |
+
FOREIGN KEY (telegram_id) REFERENCES users(telegram_id) ON DELETE CASCADE
|
| 37 |
+
);
|
| 38 |
+
|
| 39 |
+
-- Indexes for better performance
|
| 40 |
+
CREATE INDEX IF NOT EXISTS idx_messages_telegram_id_created_at ON messages(telegram_id, created_at);
|
| 41 |
+
CREATE INDEX IF NOT EXISTS idx_users_telegram_id ON users(telegram_id);
|
| 42 |
+
CREATE INDEX IF NOT EXISTS idx_sessions_telegram_id ON conversation_sessions(telegram_id);
|
| 43 |
+
|
| 44 |
+
-- Function to update updated_at timestamp
|
| 45 |
+
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
| 46 |
+
RETURNS TRIGGER AS $$
|
| 47 |
+
BEGIN
|
| 48 |
+
NEW.updated_at = NOW();
|
| 49 |
+
RETURN NEW;
|
| 50 |
+
END;
|
| 51 |
+
$$ language 'plpgsql';
|
| 52 |
+
|
| 53 |
+
-- Trigger to update user's updated_at timestamp
|
| 54 |
+
CREATE TRIGGER update_user_updated_at
|
| 55 |
+
BEFORE UPDATE ON users
|
| 56 |
+
FOR EACH ROW
|
| 57 |
+
EXECUTE FUNCTION update_updated_at_column();
|
| 58 |
+
|
| 59 |
+
-- Trigger to update session message count
|
| 60 |
+
CREATE OR REPLACE FUNCTION update_session_count()
|
| 61 |
+
RETURNS TRIGGER AS $$
|
| 62 |
+
BEGIN
|
| 63 |
+
UPDATE conversation_sessions
|
| 64 |
+
SET message_count = message_count + 1
|
| 65 |
+
WHERE telegram_id = NEW.telegram_id AND session_end IS NULL;
|
| 66 |
+
RETURN NEW;
|
| 67 |
+
END;
|
| 68 |
+
$$ language 'plpgsql';
|
| 69 |
+
|
| 70 |
+
CREATE TRIGGER update_session_message_count
|
| 71 |
+
AFTER INSERT ON messages
|
| 72 |
+
FOR EACH ROW
|
| 73 |
+
EXECUTE FUNCTION update_session_count();
|
| 74 |
+
|
| 75 |
+
-- Row Level Security (RLS) policies
|
| 76 |
+
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
|
| 77 |
+
ALTER TABLE messages ENABLE ROW LEVEL SECURITY;
|
| 78 |
+
ALTER TABLE conversation_sessions ENABLE ROW LEVEL SECURITY;
|
| 79 |
+
|
| 80 |
+
-- Policy for users table (allow all operations for now)
|
| 81 |
+
CREATE POLICY "Enable all operations for users" ON users
|
| 82 |
+
FOR ALL USING (true);
|
| 83 |
+
|
| 84 |
+
-- Policy for messages table (allow all operations for now)
|
| 85 |
+
CREATE POLICY "Enable all operations for messages" ON messages
|
| 86 |
+
FOR ALL USING (true);
|
| 87 |
+
|
| 88 |
+
-- Policy for conversation_sessions table (allow all operations for now)
|
| 89 |
+
CREATE POLICY "Enable all operations for conversation_sessions" ON conversation_sessions
|
| 90 |
+
FOR ALL USING (true);
|
telegram_handlers.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import Request
|
| 2 |
+
import httpx
|
| 3 |
+
from config import TELEGRAM_URL
|
| 4 |
+
from ai_service import get_ai_response
|
| 5 |
+
from database import db_manager
|
| 6 |
+
|
| 7 |
+
async def telegram_webhook(request: Request):
|
| 8 |
+
data = await request.json()
|
| 9 |
+
|
| 10 |
+
if "message" in data:
|
| 11 |
+
telegram_id = data["message"]["chat"]["id"]
|
| 12 |
+
user_text = data["message"].get("text", "")
|
| 13 |
+
|
| 14 |
+
# Extract user info
|
| 15 |
+
user_info = data["message"]["chat"]
|
| 16 |
+
username = user_info.get("username")
|
| 17 |
+
first_name = user_info.get("first_name")
|
| 18 |
+
last_name = user_info.get("last_name")
|
| 19 |
+
|
| 20 |
+
if user_text:
|
| 21 |
+
# Save user message to database if available
|
| 22 |
+
if db_manager:
|
| 23 |
+
db_manager.save_message(telegram_id, user_text, "user")
|
| 24 |
+
db_manager.create_or_update_user(telegram_id, username, first_name, last_name)
|
| 25 |
+
|
| 26 |
+
# Get the intelligent response with conversation history
|
| 27 |
+
ai_answer = await get_ai_response(user_text, telegram_id)
|
| 28 |
+
|
| 29 |
+
# Save assistant response to database if available
|
| 30 |
+
if db_manager:
|
| 31 |
+
db_manager.save_message(telegram_id, ai_answer, "assistant")
|
| 32 |
+
|
| 33 |
+
# Send back to Telegram if TELEGRAM_URL is available
|
| 34 |
+
if TELEGRAM_URL:
|
| 35 |
+
async with httpx.AsyncClient(verify=False) as client:
|
| 36 |
+
await client.post(
|
| 37 |
+
TELEGRAM_URL,
|
| 38 |
+
headers={"Host": "api.telegram.org"},
|
| 39 |
+
json={
|
| 40 |
+
"chat_id": telegram_id,
|
| 41 |
+
"text": ai_answer,
|
| 42 |
+
"parse_mode": "Markdown"
|
| 43 |
+
}
|
| 44 |
+
)
|
| 45 |
+
|
| 46 |
+
return {"status": "ok"}
|
| 47 |
+
|
| 48 |
+
async def test_webhook(request: Request):
|
| 49 |
+
data = await request.json()
|
| 50 |
+
telegram_id = data["message"]["chat"]["id"]
|
| 51 |
+
user_text = data["message"]["text"]
|
| 52 |
+
|
| 53 |
+
# Save user message to database if available
|
| 54 |
+
if db_manager:
|
| 55 |
+
db_manager.save_message(telegram_id, user_text, "user")
|
| 56 |
+
|
| 57 |
+
# Get response with conversation history
|
| 58 |
+
response = await get_ai_response(user_text, telegram_id)
|
| 59 |
+
|
| 60 |
+
# Save assistant response to database if available
|
| 61 |
+
if db_manager:
|
| 62 |
+
db_manager.save_message(telegram_id, response, "assistant")
|
| 63 |
+
|
| 64 |
+
return {"response": response}
|
utils.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import socket
|
| 2 |
+
|
| 3 |
+
async def dns_test():
|
| 4 |
+
try:
|
| 5 |
+
ip = socket.gethostbyname("api.telegram.org")
|
| 6 |
+
return {"resolved_ip": ip}
|
| 7 |
+
except Exception as e:
|
| 8 |
+
return {"error": str(e)}
|
| 9 |
+
|
| 10 |
+
async def test_ai_response():
|
| 11 |
+
try:
|
| 12 |
+
from ai_service import get_ai_response
|
| 13 |
+
response = await get_ai_response("عن ماذا كنت اسال", 12)
|
| 14 |
+
return {"response": response}
|
| 15 |
+
except Exception as e:
|
| 16 |
+
return {"error": str(e)}
|