Update app.py
Browse files
app.py
CHANGED
|
@@ -54,6 +54,7 @@ class UserProfile(Base):
|
|
| 54 |
__tablename__ = "user_profiles"
|
| 55 |
id = Column(Integer, primary_key=True, index=True)
|
| 56 |
user_id = Column(String, unique=True, index=True)
|
|
|
|
| 57 |
name = Column(String, default="Valued Customer")
|
| 58 |
email = Column(String, default="unknown@example.com")
|
| 59 |
preferences = Column(Text, default="") # e.g., favorite dishes, dietary restrictions
|
|
@@ -241,8 +242,8 @@ def process_order_flow(user_id: str, message: str) -> str:
|
|
| 241 |
asyncio.create_task(save_order())
|
| 242 |
# Clear conversation state for order flow.
|
| 243 |
del user_state[user_id]
|
| 244 |
-
#
|
| 245 |
-
email = "customer@example.com"
|
| 246 |
payment_data = create_paystack_payment_link(email, total_price * 100, order_id)
|
| 247 |
if payment_data.get("status"):
|
| 248 |
payment_link = payment_data["data"]["authorization_url"]
|
|
@@ -257,15 +258,19 @@ def process_order_flow(user_id: str, message: str) -> str:
|
|
| 257 |
return ""
|
| 258 |
|
| 259 |
# --- User Profile Functions ---
|
| 260 |
-
async def get_or_create_user_profile(user_id: str) -> UserProfile:
|
| 261 |
-
"""Retrieve an existing profile or create a new one."""
|
| 262 |
async with async_session() as session:
|
| 263 |
result = await session.execute(
|
| 264 |
UserProfile.__table__.select().where(UserProfile.user_id == user_id)
|
| 265 |
)
|
| 266 |
profile = result.scalar_one_or_none()
|
| 267 |
if profile is None:
|
| 268 |
-
profile = UserProfile(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
session.add(profile)
|
| 270 |
await session.commit()
|
| 271 |
return profile
|
|
@@ -284,8 +289,6 @@ async def update_user_last_interaction(user_id: str):
|
|
| 284 |
# --- Proactive Engagement: Warm Greetings ---
|
| 285 |
async def send_proactive_greeting(user_id: str):
|
| 286 |
"""Simulate sending a proactive greeting if the user has been inactive."""
|
| 287 |
-
# In a real system, you might schedule this using a job scheduler.
|
| 288 |
-
# Here we just simulate a warm greeting message.
|
| 289 |
greeting = "Hi again! We miss you. Would you like to see our new menu items or get personalized recommendations?"
|
| 290 |
await log_chat_to_db(user_id, "outbound", greeting)
|
| 291 |
return greeting
|
|
@@ -305,11 +308,13 @@ async def chatbot_response(request: Request, background_tasks: BackgroundTasks):
|
|
| 305 |
Supports text queries, image queries, and advanced logic.
|
| 306 |
Expects JSON payload with:
|
| 307 |
- 'user_id'
|
|
|
|
| 308 |
- 'message'
|
| 309 |
- Optionally, 'is_image': true and 'image_base64'
|
| 310 |
"""
|
| 311 |
data = await request.json()
|
| 312 |
user_id = data.get("user_id")
|
|
|
|
| 313 |
user_message = data.get("message", "").strip()
|
| 314 |
is_image = data.get("is_image", False)
|
| 315 |
image_b64 = data.get("image_base64", None)
|
|
@@ -319,11 +324,11 @@ async def chatbot_response(request: Request, background_tasks: BackgroundTasks):
|
|
| 319 |
|
| 320 |
# Log inbound message
|
| 321 |
background_tasks.add_task(log_chat_to_db, user_id, "inbound", user_message)
|
| 322 |
-
# Update user last interaction and profile
|
| 323 |
await update_user_last_interaction(user_id)
|
| 324 |
-
await get_or_create_user_profile(user_id)
|
| 325 |
|
| 326 |
-
# Handle
|
| 327 |
if is_image and image_b64:
|
| 328 |
if len(image_b64) >= 180_000:
|
| 329 |
raise HTTPException(status_code=400, detail="Image too large.")
|
|
@@ -334,7 +339,7 @@ async def chatbot_response(request: Request, background_tasks: BackgroundTasks):
|
|
| 334 |
sentiment_score = analyze_sentiment(user_message)
|
| 335 |
background_tasks.add_task(log_sentiment, user_id, user_message, sentiment_score)
|
| 336 |
|
| 337 |
-
# Adjust response tone based on sentiment
|
| 338 |
sentiment_modifier = ""
|
| 339 |
if sentiment_score < -0.3:
|
| 340 |
sentiment_modifier = "I'm sorry if you're having a tough time. "
|
|
@@ -426,6 +431,7 @@ async def get_user_profile(user_id: str):
|
|
| 426 |
profile = await get_or_create_user_profile(user_id)
|
| 427 |
return {
|
| 428 |
"user_id": profile.user_id,
|
|
|
|
| 429 |
"name": profile.name,
|
| 430 |
"email": profile.email,
|
| 431 |
"preferences": profile.preferences,
|
|
@@ -465,9 +471,8 @@ async def process_voice(file: UploadFile = File(...)):
|
|
| 465 |
Accept a voice file upload, perform speech-to-text (simulated), and process the resulting text.
|
| 466 |
In production, integrate with a real STT service.
|
| 467 |
"""
|
| 468 |
-
# Simulated Speech-to-Text: read file bytes and decode (for demo, just return a fixed string)
|
| 469 |
contents = await file.read()
|
| 470 |
-
# In real implementation, send `contents` to an STT service.
|
| 471 |
simulated_text = "Simulated speech-to-text conversion result."
|
| 472 |
return {"transcription": simulated_text}
|
| 473 |
|
|
@@ -480,7 +485,7 @@ async def payment_callback(request: Request):
|
|
| 480 |
"""
|
| 481 |
data = await request.json()
|
| 482 |
# Extract order reference and update order status accordingly.
|
| 483 |
-
#
|
| 484 |
order_id = data.get("reference")
|
| 485 |
new_status = data.get("status", "Paid")
|
| 486 |
async with async_session() as session:
|
|
|
|
| 54 |
__tablename__ = "user_profiles"
|
| 55 |
id = Column(Integer, primary_key=True, index=True)
|
| 56 |
user_id = Column(String, unique=True, index=True)
|
| 57 |
+
phone_number = Column(String, unique=True, index=True, nullable=True) # New field for phone numbers
|
| 58 |
name = Column(String, default="Valued Customer")
|
| 59 |
email = Column(String, default="unknown@example.com")
|
| 60 |
preferences = Column(Text, default="") # e.g., favorite dishes, dietary restrictions
|
|
|
|
| 242 |
asyncio.create_task(save_order())
|
| 243 |
# Clear conversation state for order flow.
|
| 244 |
del user_state[user_id]
|
| 245 |
+
# Retrieve email from user profile if available (here using a placeholder)
|
| 246 |
+
email = "customer@example.com"
|
| 247 |
payment_data = create_paystack_payment_link(email, total_price * 100, order_id)
|
| 248 |
if payment_data.get("status"):
|
| 249 |
payment_link = payment_data["data"]["authorization_url"]
|
|
|
|
| 258 |
return ""
|
| 259 |
|
| 260 |
# --- User Profile Functions ---
|
| 261 |
+
async def get_or_create_user_profile(user_id: str, phone_number: str = None) -> UserProfile:
|
| 262 |
+
"""Retrieve an existing profile or create a new one with user_id and phone_number."""
|
| 263 |
async with async_session() as session:
|
| 264 |
result = await session.execute(
|
| 265 |
UserProfile.__table__.select().where(UserProfile.user_id == user_id)
|
| 266 |
)
|
| 267 |
profile = result.scalar_one_or_none()
|
| 268 |
if profile is None:
|
| 269 |
+
profile = UserProfile(
|
| 270 |
+
user_id=user_id,
|
| 271 |
+
phone_number=phone_number,
|
| 272 |
+
last_interaction=datetime.utcnow()
|
| 273 |
+
)
|
| 274 |
session.add(profile)
|
| 275 |
await session.commit()
|
| 276 |
return profile
|
|
|
|
| 289 |
# --- Proactive Engagement: Warm Greetings ---
|
| 290 |
async def send_proactive_greeting(user_id: str):
|
| 291 |
"""Simulate sending a proactive greeting if the user has been inactive."""
|
|
|
|
|
|
|
| 292 |
greeting = "Hi again! We miss you. Would you like to see our new menu items or get personalized recommendations?"
|
| 293 |
await log_chat_to_db(user_id, "outbound", greeting)
|
| 294 |
return greeting
|
|
|
|
| 308 |
Supports text queries, image queries, and advanced logic.
|
| 309 |
Expects JSON payload with:
|
| 310 |
- 'user_id'
|
| 311 |
+
- 'phone_number'
|
| 312 |
- 'message'
|
| 313 |
- Optionally, 'is_image': true and 'image_base64'
|
| 314 |
"""
|
| 315 |
data = await request.json()
|
| 316 |
user_id = data.get("user_id")
|
| 317 |
+
phone_number = data.get("phone_number") # New field for phone number
|
| 318 |
user_message = data.get("message", "").strip()
|
| 319 |
is_image = data.get("is_image", False)
|
| 320 |
image_b64 = data.get("image_base64", None)
|
|
|
|
| 324 |
|
| 325 |
# Log inbound message
|
| 326 |
background_tasks.add_task(log_chat_to_db, user_id, "inbound", user_message)
|
| 327 |
+
# Update user last interaction and create profile with phone number
|
| 328 |
await update_user_last_interaction(user_id)
|
| 329 |
+
await get_or_create_user_profile(user_id, phone_number)
|
| 330 |
|
| 331 |
+
# Handle image queries
|
| 332 |
if is_image and image_b64:
|
| 333 |
if len(image_b64) >= 180_000:
|
| 334 |
raise HTTPException(status_code=400, detail="Image too large.")
|
|
|
|
| 339 |
sentiment_score = analyze_sentiment(user_message)
|
| 340 |
background_tasks.add_task(log_sentiment, user_id, user_message, sentiment_score)
|
| 341 |
|
| 342 |
+
# Adjust response tone based on sentiment
|
| 343 |
sentiment_modifier = ""
|
| 344 |
if sentiment_score < -0.3:
|
| 345 |
sentiment_modifier = "I'm sorry if you're having a tough time. "
|
|
|
|
| 431 |
profile = await get_or_create_user_profile(user_id)
|
| 432 |
return {
|
| 433 |
"user_id": profile.user_id,
|
| 434 |
+
"phone_number": profile.phone_number,
|
| 435 |
"name": profile.name,
|
| 436 |
"email": profile.email,
|
| 437 |
"preferences": profile.preferences,
|
|
|
|
| 471 |
Accept a voice file upload, perform speech-to-text (simulated), and process the resulting text.
|
| 472 |
In production, integrate with a real STT service.
|
| 473 |
"""
|
|
|
|
| 474 |
contents = await file.read()
|
| 475 |
+
# Simulated Speech-to-Text: In real implementation, send `contents` to an STT service.
|
| 476 |
simulated_text = "Simulated speech-to-text conversion result."
|
| 477 |
return {"transcription": simulated_text}
|
| 478 |
|
|
|
|
| 485 |
"""
|
| 486 |
data = await request.json()
|
| 487 |
# Extract order reference and update order status accordingly.
|
| 488 |
+
# In production, verify callback signature and extract data.
|
| 489 |
order_id = data.get("reference")
|
| 490 |
new_status = data.get("status", "Paid")
|
| 491 |
async with async_session() as session:
|