Fred808 commited on
Commit
65a6738
·
verified ·
1 Parent(s): 1f27208

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +94 -16
app.py CHANGED
@@ -9,7 +9,7 @@ from bs4 import BeautifulSoup
9
  from sqlalchemy import select
10
 
11
  from fastapi import FastAPI, Request, HTTPException, BackgroundTasks, UploadFile, File, Form
12
- from fastapi.responses import JSONResponse, StreamingResponse
13
 
14
  import openai
15
 
@@ -28,6 +28,11 @@ DATABASE_URL = os.getenv("DATABASE_URL", "postgresql+asyncpg://postgres.lgbnxply
28
  NVIDIA_API_KEY = os.getenv("NVIDIA_API_KEY", "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")
29
  openai.api_key = os.getenv("OPENAI_API_KEY", "your_openai_api_key")
30
 
 
 
 
 
 
31
  # --- Database Setup ---
32
  Base = declarative_base()
33
 
@@ -78,7 +83,6 @@ async def init_db():
78
  await conn.run_sync(Base.metadata.create_all)
79
 
80
  # --- Global In-Memory Stores ---
81
- # Instead of a plain dict for conversation context, we use a dedicated class below.
82
  user_state = {} # e.g., { user_id: ConversationState }
83
  conversation_context = {} # { user_id: [ { "timestamp": ..., "role": "user"/"bot", "message": ... }, ... ] }
84
  proactive_timer = {}
@@ -166,6 +170,26 @@ def create_paystack_payment_link(email: str, amount: int, reference: str) -> dic
166
  except Exception as e:
167
  return {"status": False, "message": str(e)}
168
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  # --- NVIDIA LLM Streaming Functions ---
170
  def stream_text_completion(prompt: str):
171
  from openai import OpenAI
@@ -316,6 +340,28 @@ def process_order_flow(user_id: str, message: str) -> str:
316
  await session.commit()
317
  asyncio.create_task(save_order())
318
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
319
  email = "customer@example.com" # Placeholder; retrieve from profile if available
320
  payment_data = create_paystack_payment_link(email, total_price * 100, order_id)
321
  dish_name = state.data.get("dish", "")
@@ -580,22 +626,54 @@ async def process_voice(file: UploadFile = File(...)):
580
  simulated_text = "Simulated speech-to-text conversion result."
581
  return {"transcription": simulated_text}
582
 
583
- @app.post("/payment_callback")
 
584
  async def payment_callback(request: Request):
585
- data = await request.json()
586
- order_id = data.get("reference")
587
- new_status = data.get("status", "Paid")
588
- async with async_session() as session:
589
- result = await session.execute(
590
- Order.__table__.select().where(Order.order_id == order_id)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
591
  )
592
- order = result.scalar_one_or_none()
593
- if order:
594
- order.status = new_status
595
- await session.commit()
596
- return JSONResponse(content={"message": "Order updated successfully."})
597
- else:
598
- raise HTTPException(status_code=404, detail="Order not found.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
599
 
600
  if __name__ == "__main__":
601
  import uvicorn
 
9
  from sqlalchemy import select
10
 
11
  from fastapi import FastAPI, Request, HTTPException, BackgroundTasks, UploadFile, File, Form
12
+ from fastapi.responses import JSONResponse, StreamingResponse, RedirectResponse
13
 
14
  import openai
15
 
 
28
  NVIDIA_API_KEY = os.getenv("NVIDIA_API_KEY", "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")
29
  openai.api_key = os.getenv("OPENAI_API_KEY", "your_openai_api_key")
30
 
31
+ # WhatsApp Business API credentials (Cloud API)
32
+ WHATSAPP_PHONE_NUMBER_ID = os.getenv("WHATSAPP_PHONE_NUMBER_ID", "505165592686490")
33
+ WHATSAPP_ACCESS_TOKEN = os.getenv("WHATSAPP_ACCESS_TOKEN", "EAANi3PCgUmQBO9HKRZA8afKesVL7trAlZAqsFlrebFZBEilHWKKBp4H8Xw2eb2GdRTUZCeOA9lsNmm6zFO19kFq7uJdTTZCq9HrJJoGgNvzc0iehNuLaVdHd8AhZConIoAwLY7MlaA1nc0OKJ0Lw5LhFvJN3ZCuJLP3NskPn42v6HPkFnbFdXeqfJx9iosk4sOQ")
34
+ MANAGEMENT_WHATSAPP_NUMBER = os.getenv("MANAGEMENT_WHATSAPP_NUMBER", "2348035105411") # e.g. "15551234567" (international format without prefix)
35
+
36
  # --- Database Setup ---
37
  Base = declarative_base()
38
 
 
83
  await conn.run_sync(Base.metadata.create_all)
84
 
85
  # --- Global In-Memory Stores ---
 
86
  user_state = {} # e.g., { user_id: ConversationState }
87
  conversation_context = {} # { user_id: [ { "timestamp": ..., "role": "user"/"bot", "message": ... }, ... ] }
88
  proactive_timer = {}
 
170
  except Exception as e:
171
  return {"status": False, "message": str(e)}
172
 
173
+ # --- WhatsApp Business API Helper ---
174
+ def send_whatsapp_message(recipient: str, message_body: str) -> dict:
175
+ """
176
+ Sends a WhatsApp text message using the WhatsApp Cloud API.
177
+ `recipient` should be in international format, e.g., "15551234567".
178
+ """
179
+ url = f"https://graph.facebook.com/v15.0/{WHATSAPP_PHONE_NUMBER_ID}/messages"
180
+ headers = {
181
+ "Authorization": f"Bearer {WHATSAPP_ACCESS_TOKEN}",
182
+ "Content-Type": "application/json"
183
+ }
184
+ payload = {
185
+ "messaging_product": "whatsapp",
186
+ "to": recipient,
187
+ "type": "text",
188
+ "text": {"body": message_body}
189
+ }
190
+ response = requests.post(url, headers=headers, json=payload)
191
+ return response.json()
192
+
193
  # --- NVIDIA LLM Streaming Functions ---
194
  def stream_text_completion(prompt: str):
195
  from openai import OpenAI
 
340
  await session.commit()
341
  asyncio.create_task(save_order())
342
 
343
+ # Notify management of the new order via WhatsApp
344
+ async def notify_management_order(order_details: dict):
345
+ message_body = (
346
+ f"New Order Received:\n"
347
+ f"Order ID: {order_details['order_id']}\n"
348
+ f"Dish: {order_details['dish']}\n"
349
+ f"Quantity: {order_details['quantity']}\n"
350
+ f"Total Price: {order_details['price']}\n"
351
+ f"Delivery Address: {order_details.get('address', 'Not Provided')}\n"
352
+ f"Status: Pending Payment"
353
+ )
354
+ await asyncio.to_thread(send_whatsapp_message, MANAGEMENT_WHATSAPP_NUMBER, message_body)
355
+ order_details = {
356
+ "order_id": order_id,
357
+ "dish": state.data["dish"],
358
+ "quantity": state.data["quantity"],
359
+ "price": state.data["price"],
360
+ "address": state.data.get("address", ""),
361
+ "status": "Pending Payment"
362
+ }
363
+ asyncio.create_task(notify_management_order(order_details))
364
+
365
  email = "customer@example.com" # Placeholder; retrieve from profile if available
366
  payment_data = create_paystack_payment_link(email, total_price * 100, order_id)
367
  dish_name = state.data.get("dish", "")
 
626
  simulated_text = "Simulated speech-to-text conversion result."
627
  return {"transcription": simulated_text}
628
 
629
+ # --- Payment Callback Endpoint with Payment Tracking and Redirection ---
630
+ @app.api_route("/payment_callback", methods=["GET", "POST"])
631
  async def payment_callback(request: Request):
632
+ # GET: User redirection after payment
633
+ if request.method == "GET":
634
+ params = request.query_params
635
+ order_id = params.get("reference")
636
+ status = params.get("status", "Paid")
637
+ if not order_id:
638
+ raise HTTPException(status_code=400, detail="Missing order reference in callback.")
639
+ async with async_session() as session:
640
+ result = await session.execute(
641
+ Order.__table__.select().where(Order.order_id == order_id)
642
+ )
643
+ order = result.scalar_one_or_none()
644
+ if order:
645
+ order.status = status
646
+ await session.commit()
647
+ else:
648
+ raise HTTPException(status_code=404, detail="Order not found.")
649
+ # Notify management via WhatsApp about the payment update
650
+ await asyncio.to_thread(send_whatsapp_message, MANAGEMENT_WHATSAPP_NUMBER,
651
+ f"Payment Update:\nOrder ID: {order_id} is now {status}."
652
  )
653
+ # Redirect user back to the chat interface (adjust URL as needed)
654
+ redirect_url = f"https://yourdomain.com/chat?order_id={order_id}&status=success"
655
+ return RedirectResponse(url=redirect_url)
656
+ # POST: Server-to-server callback from Paystack
657
+ else:
658
+ data = await request.json()
659
+ order_id = data.get("reference")
660
+ new_status = data.get("status", "Paid")
661
+ if not order_id:
662
+ raise HTTPException(status_code=400, detail="Missing order reference in callback.")
663
+ async with async_session() as session:
664
+ result = await session.execute(
665
+ Order.__table__.select().where(Order.order_id == order_id)
666
+ )
667
+ order = result.scalar_one_or_none()
668
+ if order:
669
+ order.status = new_status
670
+ await session.commit()
671
+ await asyncio.to_thread(send_whatsapp_message, MANAGEMENT_WHATSAPP_NUMBER,
672
+ f"Payment Update:\nOrder ID: {order_id} is now {new_status}."
673
+ )
674
+ return JSONResponse(content={"message": "Order updated successfully."})
675
+ else:
676
+ raise HTTPException(status_code=404, detail="Order not found.")
677
 
678
  if __name__ == "__main__":
679
  import uvicorn