zenaight commited on
Commit ·
851cd13
1
Parent(s): 2d92890
supabase
Browse files- README.md +124 -11
- main.py +130 -7
- requirements.txt +2 -1
- supabase_setup.sql +33 -0
README.md
CHANGED
|
@@ -1,11 +1,124 @@
|
|
| 1 |
-
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
-
|
| 10 |
-
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# PropAgent - WhatsApp AI Agent
|
| 2 |
+
|
| 3 |
+
A WhatsApp chatbot powered by OpenAI GPT-4 and integrated with Supabase for user management.
|
| 4 |
+
|
| 5 |
+
## Features
|
| 6 |
+
|
| 7 |
+
- 🤖 AI-powered chat responses using OpenAI GPT-4
|
| 8 |
+
- 📱 WhatsApp Business API integration
|
| 9 |
+
- 👥 User management with Supabase database
|
| 10 |
+
- 🔄 Conversation memory and context
|
| 11 |
+
- 📊 User activity tracking
|
| 12 |
+
- 🚀 RESTful API endpoints for user management
|
| 13 |
+
|
| 14 |
+
## Setup
|
| 15 |
+
|
| 16 |
+
### 1. Environment Variables
|
| 17 |
+
|
| 18 |
+
Create a `.env` file with the following variables:
|
| 19 |
+
|
| 20 |
+
```bash
|
| 21 |
+
# WhatsApp Business API
|
| 22 |
+
VERIFY_TOKEN=your_webhook_verify_token
|
| 23 |
+
PHONE_NUMBER_ID=your_phone_number_id
|
| 24 |
+
WHATSAPP_API_TOKEN=your_whatsapp_api_token
|
| 25 |
+
|
| 26 |
+
# OpenAI
|
| 27 |
+
OPENAI_API_KEY=your_openai_api_key
|
| 28 |
+
|
| 29 |
+
# Supabase
|
| 30 |
+
SUPABASE_URL=your_supabase_project_url
|
| 31 |
+
SUPABASE_KEY=your_supabase_anon_key
|
| 32 |
+
```
|
| 33 |
+
|
| 34 |
+
### 2. Supabase Setup
|
| 35 |
+
|
| 36 |
+
1. Create a new Supabase project at [supabase.com](https://supabase.com)
|
| 37 |
+
2. Go to the SQL Editor in your Supabase dashboard
|
| 38 |
+
3. Run the SQL script from `supabase_setup.sql` to create the users table
|
| 39 |
+
4. Copy your project URL and anon key from Settings > API
|
| 40 |
+
|
| 41 |
+
### 3. WhatsApp Business API Setup
|
| 42 |
+
|
| 43 |
+
1. Create a Meta Developer account
|
| 44 |
+
2. Set up a WhatsApp Business app
|
| 45 |
+
3. Configure webhook URL: `https://your-domain.com/webhook`
|
| 46 |
+
4. Set the verify token in your environment variables
|
| 47 |
+
|
| 48 |
+
### 4. Installation
|
| 49 |
+
|
| 50 |
+
```bash
|
| 51 |
+
pip install -r requirements.txt
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
### 5. Running the Application
|
| 55 |
+
|
| 56 |
+
```bash
|
| 57 |
+
uvicorn main:app --host 0.0.0.0 --port 8000
|
| 58 |
+
```
|
| 59 |
+
|
| 60 |
+
## API Endpoints
|
| 61 |
+
|
| 62 |
+
### Webhook Endpoints
|
| 63 |
+
- `GET /webhook` - WhatsApp webhook verification
|
| 64 |
+
- `POST /webhook` - Receive WhatsApp messages
|
| 65 |
+
|
| 66 |
+
### User Management
|
| 67 |
+
- `GET /users/{wa_id}` - Get user by WhatsApp ID
|
| 68 |
+
- `GET /users` - List all users (with pagination)
|
| 69 |
+
- `PUT /users/{wa_id}` - Update user name
|
| 70 |
+
|
| 71 |
+
### Health & Testing
|
| 72 |
+
- `GET /ping` - Simple health check
|
| 73 |
+
- `GET /health` - Detailed service status
|
| 74 |
+
- `POST /chat` - Direct AI chat endpoint
|
| 75 |
+
|
| 76 |
+
## Database Schema
|
| 77 |
+
|
| 78 |
+
### Users Table
|
| 79 |
+
```sql
|
| 80 |
+
users
|
| 81 |
+
-----
|
| 82 |
+
- wa_id (TEXT, PRIMARY KEY) - WhatsApp user ID
|
| 83 |
+
- name (TEXT) - User's display name
|
| 84 |
+
- created_at (TIMESTAMP) - Account creation time
|
| 85 |
+
- updated_at (TIMESTAMP) - Last activity time
|
| 86 |
+
```
|
| 87 |
+
|
| 88 |
+
## Features
|
| 89 |
+
|
| 90 |
+
### User Management
|
| 91 |
+
- Automatic user creation when first message is received
|
| 92 |
+
- User name updates from WhatsApp profile
|
| 93 |
+
- Activity tracking with automatic timestamp updates
|
| 94 |
+
|
| 95 |
+
### AI Chat
|
| 96 |
+
- Context-aware conversations using user names
|
| 97 |
+
- Conversation memory (last 6 messages)
|
| 98 |
+
- Error handling and fallback responses
|
| 99 |
+
|
| 100 |
+
### Security
|
| 101 |
+
- Environment variable configuration
|
| 102 |
+
- Optional Row Level Security (RLS) in Supabase
|
| 103 |
+
- Input validation and error handling
|
| 104 |
+
|
| 105 |
+
## Development
|
| 106 |
+
|
| 107 |
+
The application uses:
|
| 108 |
+
- **FastAPI** for the web framework
|
| 109 |
+
- **LangGraph** for conversation flow management
|
| 110 |
+
- **OpenAI GPT-4** for AI responses
|
| 111 |
+
- **Supabase** for user data storage
|
| 112 |
+
- **WhatsApp Business API** for messaging
|
| 113 |
+
|
| 114 |
+
## Deployment
|
| 115 |
+
|
| 116 |
+
The application includes a Dockerfile for containerized deployment. You can deploy it to:
|
| 117 |
+
- Hugging Face Spaces
|
| 118 |
+
- Railway
|
| 119 |
+
- Heroku
|
| 120 |
+
- Any container platform
|
| 121 |
+
|
| 122 |
+
## License
|
| 123 |
+
|
| 124 |
+
MIT License
|
main.py
CHANGED
|
@@ -11,6 +11,8 @@ from langchain_core.memory import BaseMemory
|
|
| 11 |
from langchain_core.chat_history import BaseChatMessageHistory
|
| 12 |
from langchain_core.messages import HumanMessage, AIMessage
|
| 13 |
from langchain_openai import ChatOpenAI
|
|
|
|
|
|
|
| 14 |
|
| 15 |
# --- App and Config ---
|
| 16 |
app = FastAPI()
|
|
@@ -18,6 +20,57 @@ VERIFY_TOKEN = os.getenv("VERIFY_TOKEN", "")
|
|
| 18 |
PHONE_NUMBER_ID = os.getenv("PHONE_NUMBER_ID", "")
|
| 19 |
WHATSAPP_API_TOKEN = os.getenv("WHATSAPP_API_TOKEN", "")
|
| 20 |
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
# --- OpenAI Client Setup ---
|
| 23 |
llm = ChatOpenAI(openai_api_key=OPENAI_API_KEY, model="gpt-4o-mini")
|
|
@@ -32,6 +85,7 @@ if not OPENAI_API_KEY:
|
|
| 32 |
# --- LangGraph Setup ---
|
| 33 |
def chat_with_memory(state):
|
| 34 |
user_message = state["user_message"]
|
|
|
|
| 35 |
|
| 36 |
# Add user message to history
|
| 37 |
chat_history.append({"role": "user", "content": user_message})
|
|
@@ -39,8 +93,12 @@ def chat_with_memory(state):
|
|
| 39 |
# Keep only last 6 messages for context
|
| 40 |
recent_messages = chat_history[-6:] if len(chat_history) > 6 else chat_history
|
| 41 |
|
| 42 |
-
# Add system message
|
| 43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
|
| 45 |
try:
|
| 46 |
if not OPENAI_API_KEY:
|
|
@@ -62,6 +120,7 @@ from typing import TypedDict
|
|
| 62 |
class ChatState(TypedDict):
|
| 63 |
user_message: str
|
| 64 |
response: str
|
|
|
|
| 65 |
|
| 66 |
graph = StateGraph(ChatState)
|
| 67 |
graph.add_node("chat", RunnableLambda(chat_with_memory))
|
|
@@ -94,13 +153,26 @@ async def receive_message(req: Request):
|
|
| 94 |
return JSONResponse({"status": "ignored", "reason": "no contacts/messages"})
|
| 95 |
|
| 96 |
wa_id = contacts[0]["wa_id"]
|
|
|
|
| 97 |
msg = messages[0]
|
| 98 |
|
| 99 |
if msg.get("type") == "text":
|
| 100 |
user_message = msg.get("text", {}).get("body", "")
|
| 101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
await send_whatsapp_message(wa_id, ai_result["response"])
|
| 103 |
-
return JSONResponse({"status": "replied"})
|
| 104 |
|
| 105 |
except Exception as e:
|
| 106 |
print("Error processing message:", e)
|
|
@@ -131,7 +203,7 @@ async def send_whatsapp_message(wa_id: str, message: str):
|
|
| 131 |
class ChatRequest(BaseModel):
|
| 132 |
message: str
|
| 133 |
model: Optional[str] = "gpt-4o-mini"
|
| 134 |
-
max_tokens: Optional[int] =
|
| 135 |
|
| 136 |
class ChatResponse(BaseModel):
|
| 137 |
response: str
|
|
@@ -153,6 +225,57 @@ def health_check():
|
|
| 153 |
status = {
|
| 154 |
"api": "healthy",
|
| 155 |
"whatsapp": "configured" if WHATSAPP_API_TOKEN else "not configured",
|
| 156 |
-
"openai": "configured" if OPENAI_API_KEY else "not configured"
|
|
|
|
| 157 |
}
|
| 158 |
-
return status
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
from langchain_core.chat_history import BaseChatMessageHistory
|
| 12 |
from langchain_core.messages import HumanMessage, AIMessage
|
| 13 |
from langchain_openai import ChatOpenAI
|
| 14 |
+
from supabase import create_client, Client
|
| 15 |
+
from datetime import datetime
|
| 16 |
|
| 17 |
# --- App and Config ---
|
| 18 |
app = FastAPI()
|
|
|
|
| 20 |
PHONE_NUMBER_ID = os.getenv("PHONE_NUMBER_ID", "")
|
| 21 |
WHATSAPP_API_TOKEN = os.getenv("WHATSAPP_API_TOKEN", "")
|
| 22 |
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")
|
| 23 |
+
SUPABASE_URL = os.getenv("SUPABASE_URL", "")
|
| 24 |
+
SUPABASE_KEY = os.getenv("SUPABASE_KEY", "")
|
| 25 |
+
|
| 26 |
+
# --- Supabase Client Setup ---
|
| 27 |
+
supabase: Client = None
|
| 28 |
+
if SUPABASE_URL and SUPABASE_KEY:
|
| 29 |
+
supabase = create_client(SUPABASE_URL, SUPABASE_KEY)
|
| 30 |
+
else:
|
| 31 |
+
print("Warning: Supabase credentials not set. Database functionality will be limited.")
|
| 32 |
+
|
| 33 |
+
# --- User Management Functions ---
|
| 34 |
+
async def get_or_create_user(wa_id: str, name: str = None) -> dict:
|
| 35 |
+
"""Get existing user or create new one"""
|
| 36 |
+
if not supabase:
|
| 37 |
+
return {"wa_id": wa_id, "name": name or "Unknown"}
|
| 38 |
+
|
| 39 |
+
try:
|
| 40 |
+
# Try to get existing user
|
| 41 |
+
response = supabase.table("users").select("*").eq("wa_id", wa_id).execute()
|
| 42 |
+
|
| 43 |
+
if response.data:
|
| 44 |
+
# User exists, update if name provided
|
| 45 |
+
user = response.data[0]
|
| 46 |
+
if name and name != user.get("name"):
|
| 47 |
+
supabase.table("users").update({"name": name, "updated_at": datetime.utcnow().isoformat()}).eq("wa_id", wa_id).execute()
|
| 48 |
+
user["name"] = name
|
| 49 |
+
return user
|
| 50 |
+
else:
|
| 51 |
+
# Create new user
|
| 52 |
+
new_user = {
|
| 53 |
+
"wa_id": wa_id,
|
| 54 |
+
"name": name or "Unknown",
|
| 55 |
+
"created_at": datetime.utcnow().isoformat(),
|
| 56 |
+
"updated_at": datetime.utcnow().isoformat()
|
| 57 |
+
}
|
| 58 |
+
response = supabase.table("users").insert(new_user).execute()
|
| 59 |
+
return response.data[0] if response.data else new_user
|
| 60 |
+
|
| 61 |
+
except Exception as e:
|
| 62 |
+
print(f"Error in get_or_create_user: {e}")
|
| 63 |
+
return {"wa_id": wa_id, "name": name or "Unknown"}
|
| 64 |
+
|
| 65 |
+
async def update_user_activity(wa_id: str):
|
| 66 |
+
"""Update user's last activity timestamp"""
|
| 67 |
+
if not supabase:
|
| 68 |
+
return
|
| 69 |
+
|
| 70 |
+
try:
|
| 71 |
+
supabase.table("users").update({"updated_at": datetime.utcnow().isoformat()}).eq("wa_id", wa_id).execute()
|
| 72 |
+
except Exception as e:
|
| 73 |
+
print(f"Error updating user activity: {e}")
|
| 74 |
|
| 75 |
# --- OpenAI Client Setup ---
|
| 76 |
llm = ChatOpenAI(openai_api_key=OPENAI_API_KEY, model="gpt-4o-mini")
|
|
|
|
| 85 |
# --- LangGraph Setup ---
|
| 86 |
def chat_with_memory(state):
|
| 87 |
user_message = state["user_message"]
|
| 88 |
+
user_info = state.get("user_info", {})
|
| 89 |
|
| 90 |
# Add user message to history
|
| 91 |
chat_history.append({"role": "user", "content": user_message})
|
|
|
|
| 93 |
# Keep only last 6 messages for context
|
| 94 |
recent_messages = chat_history[-6:] if len(chat_history) > 6 else chat_history
|
| 95 |
|
| 96 |
+
# Add system message with user context
|
| 97 |
+
system_message = "You are a helpful and concise assistant."
|
| 98 |
+
if user_info.get("name") and user_info["name"] != "Unknown":
|
| 99 |
+
system_message += f" The user's name is {user_info['name']}."
|
| 100 |
+
|
| 101 |
+
messages = [{"role": "system", "content": system_message}] + recent_messages
|
| 102 |
|
| 103 |
try:
|
| 104 |
if not OPENAI_API_KEY:
|
|
|
|
| 120 |
class ChatState(TypedDict):
|
| 121 |
user_message: str
|
| 122 |
response: str
|
| 123 |
+
user_info: dict
|
| 124 |
|
| 125 |
graph = StateGraph(ChatState)
|
| 126 |
graph.add_node("chat", RunnableLambda(chat_with_memory))
|
|
|
|
| 153 |
return JSONResponse({"status": "ignored", "reason": "no contacts/messages"})
|
| 154 |
|
| 155 |
wa_id = contacts[0]["wa_id"]
|
| 156 |
+
contact_name = contacts[0].get("profile", {}).get("name")
|
| 157 |
msg = messages[0]
|
| 158 |
|
| 159 |
if msg.get("type") == "text":
|
| 160 |
user_message = msg.get("text", {}).get("body", "")
|
| 161 |
+
|
| 162 |
+
# Get or create user in database
|
| 163 |
+
user_info = await get_or_create_user(wa_id, contact_name)
|
| 164 |
+
|
| 165 |
+
# Update user activity
|
| 166 |
+
await update_user_activity(wa_id)
|
| 167 |
+
|
| 168 |
+
# Process with AI including user context
|
| 169 |
+
ai_result = await chat_graph.ainvoke({
|
| 170 |
+
"user_message": user_message,
|
| 171 |
+
"user_info": user_info
|
| 172 |
+
})
|
| 173 |
+
|
| 174 |
await send_whatsapp_message(wa_id, ai_result["response"])
|
| 175 |
+
return JSONResponse({"status": "replied", "user_id": wa_id})
|
| 176 |
|
| 177 |
except Exception as e:
|
| 178 |
print("Error processing message:", e)
|
|
|
|
| 203 |
class ChatRequest(BaseModel):
|
| 204 |
message: str
|
| 205 |
model: Optional[str] = "gpt-4o-mini"
|
| 206 |
+
max_tokens: Optional[int] = 1500
|
| 207 |
|
| 208 |
class ChatResponse(BaseModel):
|
| 209 |
response: str
|
|
|
|
| 225 |
status = {
|
| 226 |
"api": "healthy",
|
| 227 |
"whatsapp": "configured" if WHATSAPP_API_TOKEN else "not configured",
|
| 228 |
+
"openai": "configured" if OPENAI_API_KEY else "not configured",
|
| 229 |
+
"supabase": "configured" if supabase else "not configured"
|
| 230 |
}
|
| 231 |
+
return status
|
| 232 |
+
|
| 233 |
+
# --- User Management Endpoints ---
|
| 234 |
+
@app.get("/users/{wa_id}")
|
| 235 |
+
async def get_user(wa_id: str):
|
| 236 |
+
"""Get user by WhatsApp ID"""
|
| 237 |
+
if not supabase:
|
| 238 |
+
raise HTTPException(status_code=503, detail="Database not configured")
|
| 239 |
+
|
| 240 |
+
try:
|
| 241 |
+
response = supabase.table("users").select("*").eq("wa_id", wa_id).execute()
|
| 242 |
+
if response.data:
|
| 243 |
+
return response.data[0]
|
| 244 |
+
else:
|
| 245 |
+
raise HTTPException(status_code=404, detail="User not found")
|
| 246 |
+
except Exception as e:
|
| 247 |
+
print(f"Error getting user: {e}")
|
| 248 |
+
raise HTTPException(status_code=500, detail="Database error")
|
| 249 |
+
|
| 250 |
+
@app.get("/users")
|
| 251 |
+
async def list_users(limit: int = 50, offset: int = 0):
|
| 252 |
+
"""List all users with pagination"""
|
| 253 |
+
if not supabase:
|
| 254 |
+
raise HTTPException(status_code=503, detail="Database not configured")
|
| 255 |
+
|
| 256 |
+
try:
|
| 257 |
+
response = supabase.table("users").select("*").range(offset, offset + limit - 1).order("created_at", desc=True).execute()
|
| 258 |
+
return {"users": response.data, "count": len(response.data)}
|
| 259 |
+
except Exception as e:
|
| 260 |
+
print(f"Error listing users: {e}")
|
| 261 |
+
raise HTTPException(status_code=500, detail="Database error")
|
| 262 |
+
|
| 263 |
+
@app.put("/users/{wa_id}")
|
| 264 |
+
async def update_user(wa_id: str, name: str):
|
| 265 |
+
"""Update user name"""
|
| 266 |
+
if not supabase:
|
| 267 |
+
raise HTTPException(status_code=503, detail="Database not configured")
|
| 268 |
+
|
| 269 |
+
try:
|
| 270 |
+
response = supabase.table("users").update({
|
| 271 |
+
"name": name,
|
| 272 |
+
"updated_at": datetime.utcnow().isoformat()
|
| 273 |
+
}).eq("wa_id", wa_id).execute()
|
| 274 |
+
|
| 275 |
+
if response.data:
|
| 276 |
+
return response.data[0]
|
| 277 |
+
else:
|
| 278 |
+
raise HTTPException(status_code=404, detail="User not found")
|
| 279 |
+
except Exception as e:
|
| 280 |
+
print(f"Error updating user: {e}")
|
| 281 |
+
raise HTTPException(status_code=500, detail="Database error")
|
requirements.txt
CHANGED
|
@@ -6,4 +6,5 @@ openai
|
|
| 6 |
langchain
|
| 7 |
langchain-openai
|
| 8 |
langgraph
|
| 9 |
-
langchain-core
|
|
|
|
|
|
| 6 |
langchain
|
| 7 |
langchain-openai
|
| 8 |
langgraph
|
| 9 |
+
langchain-core
|
| 10 |
+
supabase
|
supabase_setup.sql
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- Create users table for WhatsApp AI Agent
|
| 2 |
+
CREATE TABLE IF NOT EXISTS users (
|
| 3 |
+
wa_id TEXT PRIMARY KEY,
|
| 4 |
+
name TEXT NOT NULL DEFAULT 'Unknown',
|
| 5 |
+
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
| 6 |
+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
| 7 |
+
);
|
| 8 |
+
|
| 9 |
+
-- Create index for better query performance
|
| 10 |
+
CREATE INDEX IF NOT EXISTS idx_users_created_at ON users(created_at DESC);
|
| 11 |
+
CREATE INDEX IF NOT EXISTS idx_users_updated_at ON users(updated_at DESC);
|
| 12 |
+
|
| 13 |
+
-- Enable Row Level Security (RLS) - optional but recommended
|
| 14 |
+
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
|
| 15 |
+
|
| 16 |
+
-- Create policy to allow all operations (you can restrict this based on your needs)
|
| 17 |
+
CREATE POLICY "Allow all operations on users" ON users
|
| 18 |
+
FOR ALL USING (true);
|
| 19 |
+
|
| 20 |
+
-- Optional: Create a function to automatically update the updated_at timestamp
|
| 21 |
+
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
| 22 |
+
RETURNS TRIGGER AS $$
|
| 23 |
+
BEGIN
|
| 24 |
+
NEW.updated_at = NOW();
|
| 25 |
+
RETURN NEW;
|
| 26 |
+
END;
|
| 27 |
+
$$ language 'plpgsql';
|
| 28 |
+
|
| 29 |
+
-- Create trigger to automatically update updated_at
|
| 30 |
+
CREATE TRIGGER update_users_updated_at
|
| 31 |
+
BEFORE UPDATE ON users
|
| 32 |
+
FOR EACH ROW
|
| 33 |
+
EXECUTE FUNCTION update_updated_at_column();
|