Update app.py
Browse files
app.py
CHANGED
|
@@ -134,6 +134,17 @@ menu_items = [
|
|
| 134 |
{"name": "Banga Soup", "description": "Traditional dish with catfish", "price": 5000, "nutrition": "Calories: 600 kcal, Carbs: 50g, Protein: 35g, Fat: 15g"}
|
| 135 |
]
|
| 136 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 137 |
|
| 138 |
class ConversationState:
|
| 139 |
def __init__(self):
|
|
@@ -630,11 +641,10 @@ async def process_order_flow(user_id: str, message: str) -> str:
|
|
| 630 |
return (f"Got it. {quantity} serving(s) of {dish}.\n"
|
| 631 |
"Please provide your phone number and delivery address.")
|
| 632 |
|
| 633 |
-
# 4
|
| 634 |
if state and state.flow == "order" and state.step == 3:
|
| 635 |
phone_pattern = r'(\+?\d{10,15})'
|
| 636 |
phone_match = re.search(phone_pattern, message)
|
| 637 |
-
address = None
|
| 638 |
if phone_match:
|
| 639 |
phone_number = phone_match.group(1)
|
| 640 |
address_start = phone_match.end()
|
|
@@ -649,88 +659,26 @@ async def process_order_flow(user_id: str, message: str) -> str:
|
|
| 649 |
else:
|
| 650 |
return ("Please provide both your phone number and address. "
|
| 651 |
"For example: '09162409591, 1, Iyana Isashi, Isashi, Lagos'.")
|
| 652 |
-
if
|
| 653 |
return "Thank you. Please provide your delivery address."
|
| 654 |
shipping_cost = calculate_shipping_cost(state.data["address"])
|
| 655 |
state.data["shipping_cost"] = shipping_cost
|
| 656 |
-
|
|
|
|
|
|
|
|
|
|
| 657 |
return (f"Thanks! Your phone number is recorded as: {state.data['phone_number']}.\n"
|
| 658 |
f"Your delivery address is: {state.data['address']}.\n"
|
| 659 |
-
f"Your delivery cost is N{shipping_cost}
|
| 660 |
-
|
| 661 |
-
# Step 5: Ask about extras.
|
| 662 |
-
if state and state.flow == "order" and state.step == 5:
|
| 663 |
-
normalized = message.strip().lower()
|
| 664 |
-
if normalized in ["yes", "y"]:
|
| 665 |
-
state.step = 6
|
| 666 |
-
return "Please list the extras you'd like (e.g., drinks, sides)."
|
| 667 |
-
elif normalized in ["no", "n"]:
|
| 668 |
-
state.data["extras"] = ""
|
| 669 |
-
state.step = 7
|
| 670 |
-
# Build order summary for confirmation:
|
| 671 |
-
if "orders" in state.data: # Multiple-dish order
|
| 672 |
-
summary = "\n".join([f"{qty} serving(s) of {dish}" for dish, qty in state.data["orders"].items()])
|
| 673 |
-
quantity_total = sum(state.data["orders"].values())
|
| 674 |
-
dish_summary = ", ".join(state.data["orders"].keys())
|
| 675 |
-
else: # Single-dish order
|
| 676 |
-
summary = f"{state.data.get('quantity', 1)} serving(s) of {state.data.get('dish', '')}"
|
| 677 |
-
quantity_total = state.data.get("quantity", 1)
|
| 678 |
-
dish_summary = state.data.get("dish", "")
|
| 679 |
-
# Determine the price correctly
|
| 680 |
-
if "dish" in state.data:
|
| 681 |
-
price_per_serving = get_dish_price(state.data["dish"])
|
| 682 |
-
total_price = (quantity_total * price_per_serving) + state.data.get("shipping_cost", 0)
|
| 683 |
-
elif "orders" in state.data:
|
| 684 |
-
total_price = 0
|
| 685 |
-
for dish, qty in state.data["orders"].items():
|
| 686 |
-
total_price += get_dish_price(dish) * qty
|
| 687 |
-
total_price += state.data.get("shipping_cost", 0)
|
| 688 |
-
else:
|
| 689 |
-
total_price = state.data.get("shipping_cost", 0)
|
| 690 |
-
confirmation = (
|
| 691 |
-
f"Order Summary:\n"
|
| 692 |
-
f"Dish(es): {dish_summary}\n"
|
| 693 |
-
f"Quantity: {quantity_total}\n"
|
| 694 |
-
f"Phone: {state.data.get('phone_number', '')}\n"
|
| 695 |
-
f"Address: {state.data.get('address', '')}\n"
|
| 696 |
-
f"Shipping Cost: N{state.data.get('shipping_cost', 0)}\n"
|
| 697 |
-
f"Total Price: N{total_price}\n"
|
| 698 |
-
f"Extras: None\nConfirm order? (yes/no)"
|
| 699 |
-
)
|
| 700 |
-
return confirmation
|
| 701 |
-
else:
|
| 702 |
-
return "Please respond with 'yes' or 'no' regarding extras."
|
| 703 |
-
|
| 704 |
-
|
| 705 |
-
# 6) Step 6: Process extras list.
|
| 706 |
-
if state and state.flow == "order" and state.step == 6:
|
| 707 |
-
state.data["extras"] = message
|
| 708 |
-
state.step = 7
|
| 709 |
-
if "orders" in state.data:
|
| 710 |
-
summary = "\n".join([f"{qty} serving(s) of {dish}" for dish, qty in state.data["orders"].items()])
|
| 711 |
-
quantity_total = sum(state.data["orders"].values())
|
| 712 |
-
dish_summary = ", ".join(state.data["orders"].keys())
|
| 713 |
-
else:
|
| 714 |
-
summary = f"{state.data.get('quantity', 1)} serving(s) of {state.data.get('dish', '')}"
|
| 715 |
-
quantity_total = state.data.get("quantity", 1)
|
| 716 |
-
dish_summary = state.data.get("dish", "")
|
| 717 |
-
price_per_serving = get_dish_price(state.data.get("dish", "")) if "dish" in state.data else 0
|
| 718 |
-
total_price = (quantity_total * price_per_serving) + state.data.get("shipping_cost", 0)
|
| 719 |
-
confirmation = (f"Order Summary:\nDish(es): {dish_summary}\nQuantity: {quantity_total}\n"
|
| 720 |
-
f"Phone: {state.data.get('phone_number', '')}\n"
|
| 721 |
-
f"Address: {state.data.get('address', '')}\n"
|
| 722 |
-
f"Shipping Cost: N{state.data.get('shipping_cost', 0)}\n"
|
| 723 |
-
f"Total Price: N{total_price}\n"
|
| 724 |
-
f"Extras: {state.data.get('extras', 'None')}\nConfirm order? (yes/no)")
|
| 725 |
-
return confirmation
|
| 726 |
|
|
|
|
| 727 |
# 7) Step 7: Order Confirmation and Payment Link Generation
|
| 728 |
if state and state.flow == "order" and state.step == 7:
|
| 729 |
if message.lower() in ["yes", "y"]:
|
| 730 |
order_id = f"ORD-{int(time.time())}"
|
| 731 |
state.data["order_id"] = order_id
|
| 732 |
-
|
| 733 |
-
# Calculate total price based on whether it's a single-dish or multi-dish order.
|
| 734 |
if "orders" in state.data:
|
| 735 |
total_price = 0
|
| 736 |
for dish, qty in state.data["orders"].items():
|
|
@@ -742,7 +690,6 @@ async def process_order_flow(user_id: str, message: str) -> str:
|
|
| 742 |
dish_summary = state.data.get("dish", "")
|
| 743 |
quantity_total = state.data.get("quantity", 1)
|
| 744 |
total_price = (quantity_total * get_dish_price(dish_summary)) + state.data.get("shipping_cost", 0)
|
| 745 |
-
|
| 746 |
state.data["price"] = str(total_price)
|
| 747 |
|
| 748 |
async def save_order():
|
|
@@ -761,6 +708,9 @@ async def process_order_flow(user_id: str, message: str) -> str:
|
|
| 761 |
asyncio.create_task(save_order())
|
| 762 |
asyncio.create_task(log_order_tracking(order_id, "Order Placed", "Order placed and awaiting payment."))
|
| 763 |
|
|
|
|
|
|
|
|
|
|
| 764 |
# Prepare order details as a dictionary for the email notification.
|
| 765 |
order_details = {
|
| 766 |
"order_id": order_id,
|
|
@@ -778,7 +728,7 @@ async def process_order_flow(user_id: str, message: str) -> str:
|
|
| 778 |
print("Failed to send email notification.")
|
| 779 |
|
| 780 |
# Generate Paystack payment link (amount in kobo)
|
| 781 |
-
email_for_paystack = "customer@example.com"
|
| 782 |
payment_data = create_paystack_payment_link(email_for_paystack, total_price * 100, order_id)
|
| 783 |
state.reset()
|
| 784 |
if user_id in user_state:
|
|
@@ -799,11 +749,13 @@ async def process_order_flow(user_id: str, message: str) -> str:
|
|
| 799 |
else:
|
| 800 |
return (f"Your order has been placed with Order ID {order_id}, "
|
| 801 |
"but we could not initialize online payment. Please try again later, or "
|
| 802 |
-
"you may opt to pay via bank transfer to Account Number 1433042821, Access Bank, Angelo Food Court 2 "
|
| 803 |
"and send your payment screenshot to this chatbot.")
|
| 804 |
elif message.lower() in ["no", "n"]:
|
| 805 |
-
|
| 806 |
-
|
|
|
|
|
|
|
| 807 |
else:
|
| 808 |
return "I didn't understand that. Please type 'yes' to confirm your order or 'no' to cancel it."
|
| 809 |
|
|
|
|
| 134 |
{"name": "Banga Soup", "description": "Traditional dish with catfish", "price": 5000, "nutrition": "Calories: 600 kcal, Carbs: 50g, Protein: 35g, Fat: 15g"}
|
| 135 |
]
|
| 136 |
|
| 137 |
+
nigerian_drinks = [
|
| 138 |
+
{"name": "Chapman", "description": "Popular Nigerian cocktail with a fruity, fizzy taste", "price": 1500, "nutrition": "Calories: 180 kcal, Carbs: 40g, Protein: 1g, Fat: 0g"},
|
| 139 |
+
{"name": "VitaMilk", "description": "Healthy, protein-rich plant-based milk", "price": 1000, "nutrition": "Calories: 180 kcal, Carbs: 20g, Protein: 8g, Fat: 6g"},
|
| 140 |
+
{"name": "Malt Drink", "description": "Non-alcoholic malt-based drink", "price": 1000, "nutrition": "Calories: 200 kcal, Carbs: 50g, Protein: 2g, Fat: 0g"},
|
| 141 |
+
{"name": "Soft Drinks", "description": "Carbonated sodas like Coke, Fanta, and Sprite", "price": 800, "nutrition": "Calories: 150 kcal, Carbs: 39g, Protein: 0g, Fat: 0g"},
|
| 142 |
+
{"name": "Bottled Water", "description": "Pure, refreshing drinking water", "price": 500, "nutrition": "Calories: 0 kcal, Carbs: 0g, Protein: 0g, Fat: 0g"},
|
| 143 |
+
{"name": "Energy Drink", "description": "Boost of energy with caffeine and vitamins", "price": 1500, "nutrition": "Calories: 200 kcal, Carbs: 50g, Protein: 1g, Fat: 0g"}
|
| 144 |
+
]
|
| 145 |
+
|
| 146 |
+
menu_items.extend(nigerian_drinks)
|
| 147 |
+
|
| 148 |
|
| 149 |
class ConversationState:
|
| 150 |
def __init__(self):
|
|
|
|
| 641 |
return (f"Got it. {quantity} serving(s) of {dish}.\n"
|
| 642 |
"Please provide your phone number and delivery address.")
|
| 643 |
|
| 644 |
+
# Step 4: Parse phone & address (for single or multi-dish orders), then skip extras.
|
| 645 |
if state and state.flow == "order" and state.step == 3:
|
| 646 |
phone_pattern = r'(\+?\d{10,15})'
|
| 647 |
phone_match = re.search(phone_pattern, message)
|
|
|
|
| 648 |
if phone_match:
|
| 649 |
phone_number = phone_match.group(1)
|
| 650 |
address_start = phone_match.end()
|
|
|
|
| 659 |
else:
|
| 660 |
return ("Please provide both your phone number and address. "
|
| 661 |
"For example: '09162409591, 1, Iyana Isashi, Isashi, Lagos'.")
|
| 662 |
+
if not state.data.get("address"):
|
| 663 |
return "Thank you. Please provide your delivery address."
|
| 664 |
shipping_cost = calculate_shipping_cost(state.data["address"])
|
| 665 |
state.data["shipping_cost"] = shipping_cost
|
| 666 |
+
# Automatically set extras to empty since we're not using extras anymore.
|
| 667 |
+
state.data["extras"] = ""
|
| 668 |
+
# Skip extras prompt and jump directly to confirmation (Step 7)
|
| 669 |
+
state.step = 7
|
| 670 |
return (f"Thanks! Your phone number is recorded as: {state.data['phone_number']}.\n"
|
| 671 |
f"Your delivery address is: {state.data['address']}.\n"
|
| 672 |
+
f"Your delivery cost is N{shipping_cost}.\n"
|
| 673 |
+
"Here is your order summary. Please confirm your order? (yes/no)")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 674 |
|
| 675 |
+
|
| 676 |
# 7) Step 7: Order Confirmation and Payment Link Generation
|
| 677 |
if state and state.flow == "order" and state.step == 7:
|
| 678 |
if message.lower() in ["yes", "y"]:
|
| 679 |
order_id = f"ORD-{int(time.time())}"
|
| 680 |
state.data["order_id"] = order_id
|
| 681 |
+
# Calculate total price:
|
|
|
|
| 682 |
if "orders" in state.data:
|
| 683 |
total_price = 0
|
| 684 |
for dish, qty in state.data["orders"].items():
|
|
|
|
| 690 |
dish_summary = state.data.get("dish", "")
|
| 691 |
quantity_total = state.data.get("quantity", 1)
|
| 692 |
total_price = (quantity_total * get_dish_price(dish_summary)) + state.data.get("shipping_cost", 0)
|
|
|
|
| 693 |
state.data["price"] = str(total_price)
|
| 694 |
|
| 695 |
async def save_order():
|
|
|
|
| 708 |
asyncio.create_task(save_order())
|
| 709 |
asyncio.create_task(log_order_tracking(order_id, "Order Placed", "Order placed and awaiting payment."))
|
| 710 |
|
| 711 |
+
# Automatically set extras to empty
|
| 712 |
+
state.data["extras"] = ""
|
| 713 |
+
|
| 714 |
# Prepare order details as a dictionary for the email notification.
|
| 715 |
order_details = {
|
| 716 |
"order_id": order_id,
|
|
|
|
| 728 |
print("Failed to send email notification.")
|
| 729 |
|
| 730 |
# Generate Paystack payment link (amount in kobo)
|
| 731 |
+
email_for_paystack = "customer@example.com"
|
| 732 |
payment_data = create_paystack_payment_link(email_for_paystack, total_price * 100, order_id)
|
| 733 |
state.reset()
|
| 734 |
if user_id in user_state:
|
|
|
|
| 749 |
else:
|
| 750 |
return (f"Your order has been placed with Order ID {order_id}, "
|
| 751 |
"but we could not initialize online payment. Please try again later, or "
|
| 752 |
+
"you may opt to pay via bank transfer to Account Number: 1433042821, Access Bank, Angelo Food Court 2 "
|
| 753 |
"and send your payment screenshot to this chatbot.")
|
| 754 |
elif message.lower() in ["no", "n"]:
|
| 755 |
+
state.reset()
|
| 756 |
+
if user_id in user_state:
|
| 757 |
+
del user_state[user_id]
|
| 758 |
+
return "Order canceled. Let me know if you'd like to try again."
|
| 759 |
else:
|
| 760 |
return "I didn't understand that. Please type 'yes' to confirm your order or 'no' to cancel it."
|
| 761 |
|