rairo commited on
Commit
eca2a9a
·
verified ·
1 Parent(s): 6d20ac3

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +168 -147
main.py CHANGED
@@ -8,11 +8,14 @@ import google.generativeai as genai
8
  from heyoo import WhatsApp
9
  from dotenv import load_dotenv
10
  from flask import Flask, request, make_response
11
- from typing import Dict, List, Optional
 
12
 
13
  # Initialize Flask App
14
  app = Flask(__name__)
15
 
 
 
16
  # Configure logging
17
  logging.basicConfig(
18
  level=logging.INFO,
@@ -27,13 +30,6 @@ load_dotenv()
27
  nameserver1 = os.getenv('nameserver1', '8.8.8.8')
28
  nameserver2 = os.getenv('nameserver2', '8.8.4.4')
29
 
30
- # Configure Gemini AI
31
- genai.configure(api_key=os.getenv("google_api_key"))
32
- gemini = genai.GenerativeModel('gemini-2.0-flash-exp')
33
-
34
- products_df = pd.read_csv("supermarket_data1.csv")
35
- user_carts: Dict[str, List[dict]] = {}
36
-
37
  # Configure global DNS resolution
38
  def setup_dns():
39
  """Configure DNS resolution globally"""
@@ -59,74 +55,6 @@ def setup_dns():
59
  # Setup DNS resolution
60
  setup_dns()
61
 
62
- def parse_user_intent(text: str) -> dict:
63
- """Use Gemini to parse user intent and context"""
64
- try:
65
- prompt = f""" You are an intelligent shopping assistant called Brave Shopper,
66
-
67
- Analyze this shopping query and return JSON with:
68
- - intent: [product_search, cart_check, checkout, other]
69
- - scenario: brief description
70
- - budget: extracted number or null
71
- - required_categories: list of relevant product categories
72
- - constraints: list of important constraints
73
-
74
- Query: {text}"""
75
-
76
- response = gemini.generate_content(prompt)
77
- return eval(response.text)
78
- except Exception as e:
79
- logger.error(f"Gemini parse error: {e}")
80
- return {"intent": "other"}
81
-
82
- def search_products(query: dict, branch: str = "Harare") -> pd.DataFrame:
83
- """Search products based on Gemini analysis"""
84
- try:
85
- filtered = products_df[
86
- (products_df["Branch"] == branch) &
87
- (products_df["Category"].isin(query.get("required_categories", []))) &
88
- (products_df["Price (USD)"] <= (query.get("budget") or float('inf')))
89
- ]
90
- return filtered.sort_values("Price (USD)").head(5)
91
- except Exception as e:
92
- logger.error(f"Search error: {e}")
93
- return pd.DataFrame()
94
-
95
- def create_cart_response(user_id: str, products: pd.DataFrame, total: float = 0):
96
- """Create interactive cart response with buttons"""
97
- products_text = "\n".join(
98
- f"{row['Product']} - ${row['Price (USD)']:.2f}"
99
- for _, row in products.iterrows()
100
- )
101
-
102
- buttons = []
103
- if not products.empty:
104
- for _, row in products.iterrows():
105
- buttons.append({
106
- "type": "reply",
107
- "reply": {"id": f"add_{row.name}", "title": f"Add {row['Product']}"}
108
- })
109
- buttons.append({"type": "reply", "reply": {"id": "cancel", "title": "Cancel"}})
110
- else:
111
- return {"text": "No products found matching your criteria."}
112
-
113
- cart = user_carts.get(user_id, [])
114
- if cart:
115
- cart_total = sum(item["Price (USD)"] for item in cart)
116
- response_text = (
117
- f"Suggested Products:\n{products_text}\n\n"
118
- f"Current Cart Total: ${cart_total:.2f}\n"
119
- "Select products to add:"
120
- )
121
- buttons.extend([
122
- {"type": "reply", "reply": {"id": "checkout", "title": "Checkout"}},
123
- {"type": "reply", "reply": {"id": "continue", "title": "Keep Shopping"}}
124
- ])
125
- else:
126
- response_text = f"Suggested Products:\n{products_text}\n\nSelect products to add:"
127
-
128
- return {"text": response_text, "buttons": buttons}
129
-
130
  # Initialize WhatsApp
131
  try:
132
  messenger = WhatsApp(
@@ -175,89 +103,185 @@ def verify_token():
175
  logger.error(f"Error in verify_token: {str(e)}")
176
  return "Internal server error", 500
177
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  @app.post("/")
179
  def hook():
180
  """Main webhook handler"""
181
  try:
182
  data = request.get_json()
 
 
 
 
 
 
 
 
 
 
 
 
183
  mobile = messenger.get_mobile(data)
 
 
184
 
185
- if messenger.is_message(data):
 
 
186
  message = messenger.get_message(data)
187
- intent = parse_user_intent(message)
188
-
189
- if intent["intent"] == "checkout":
190
- return handle_checkout(mobile)
191
-
192
- elif intent["intent"] == "cart_check":
193
- return send_cart_summary(mobile)
194
- else:
195
- products = search_products(intent)
196
- response = create_cart_response(mobile, products)
197
- messenger.send_reply_button(
198
- response["text"],
199
- response["buttons"],
200
- mobile
201
- )
202
-
203
- elif messenger.is_interactive(data):
204
- response = messenger.get_interactive_response(data)
205
- handle_button_click(mobile, response["id"])
206
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  return "OK", 200
208
 
209
  except Exception as e:
210
- logger.error(f"Webhook error: {e}")
211
  return "Internal server error", 500
212
 
213
- def handle_button_click(user_id: str, button_id: str):
214
- """Handle interactive button clicks"""
215
- if button_id.startswith("add_"):
216
- product_id = int(button_id.split("_")[1])
217
- product = products_df.loc[product_id].to_dict()
218
- user_carts.setdefault(user_id, []).append(product)
219
- messenger.send_message(f"{product['Product']} added to cart!", user_id)
220
-
221
- elif button_id == "checkout":
222
- messenger.send_interactive_button(
223
- "Confirm checkout?",
224
- [
225
- {"type": "reply", "reply": {"id": "confirm", "title": "Confirm"}},
226
- {"type": "reply", "reply": {"id": "cancel", "title": "Cancel"}}
227
- ],
228
- user_id
229
- )
230
 
231
- elif button_id == "confirm":
232
- cart = user_carts.pop(user_id, [])
233
- total = sum(item["Price (USD)"] for item in cart)
234
- messenger.send_message(
235
- f"Order confirmed! Total: ${total:.2f}\n"
236
- "Your items will be ready for pickup in 1 hour.",
237
- user_id
238
- )
239
-
240
- def send_cart_summary(user_id: str):
241
- """Send current cart summary to user"""
242
- cart = user_carts.get(user_id, [])
243
- if not cart:
244
- messenger.send_message("Your cart is empty!", user_id)
245
- return
246
-
247
- cart_text = "\n".join(
248
- f"{item['Product']} - ${item['Price (USD)']:.2f}"
249
- for item in cart
250
- )
251
- total = sum(item["Price (USD)"] for item in cart)
252
-
253
- messenger.send_interactive_button(
254
- f"Current Cart:\n{cart_text}\nTotal: ${total:.2f}",
255
- [
256
- {"type": "reply", "reply": {"id": "checkout", "title": "Checkout"}},
257
- {"type": "reply", "reply": {"id": "clear", "title": "Clear Cart"}}
258
- ],
259
- user_id
260
- )
 
 
 
261
 
262
  def test_connection():
263
  """Test DNS resolution and connection to Graph API"""
@@ -274,8 +298,5 @@ def test_connection():
274
  logger.error(f"Connection test failed: {e}")
275
 
276
  if __name__ == "__main__":
277
- # Test connection before starting
278
  test_connection()
279
-
280
- # Start Flask app
281
  app.run(debug=True, host="0.0.0.0", port=7860)
 
8
  from heyoo import WhatsApp
9
  from dotenv import load_dotenv
10
  from flask import Flask, request, make_response
11
+ from typing import Dict, List, Union
12
+ import json
13
 
14
  # Initialize Flask App
15
  app = Flask(__name__)
16
 
17
+
18
+
19
  # Configure logging
20
  logging.basicConfig(
21
  level=logging.INFO,
 
30
  nameserver1 = os.getenv('nameserver1', '8.8.8.8')
31
  nameserver2 = os.getenv('nameserver2', '8.8.4.4')
32
 
 
 
 
 
 
 
 
33
  # Configure global DNS resolution
34
  def setup_dns():
35
  """Configure DNS resolution globally"""
 
55
  # Setup DNS resolution
56
  setup_dns()
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  # Initialize WhatsApp
59
  try:
60
  messenger = WhatsApp(
 
103
  logger.error(f"Error in verify_token: {str(e)}")
104
  return "Internal server error", 500
105
 
106
+ # Load .env file
107
+ load_dotenv()
108
+
109
+ # Initialize Gemini
110
+ genai.configure(api_key=os.getenv("google_api_key"))
111
+ model = genai.GenerativeModel('gemini-2.0-flash-exp')
112
+ vision_model = genai.GenerativeModel('gemini-2.0-flash-exp')
113
+
114
+ # Load product data
115
+ try:
116
+ df = pd.read_csv("supermarket_data1")
117
+ product_data = df.to_dict('records')
118
+ except Exception as e:
119
+ logger.error(f"Failed to load product data: {str(e)}")
120
+ raise
121
+
122
+ # Cart management
123
+ carts: Dict[str, List[Dict]] = {}
124
+
125
+ class ShoppingAssistant:
126
+ def __init__(self):
127
+ self.product_data = product_data
128
+
129
+ def analyze_content(self, content: Union[str, bytes], content_type: str, budget: float = None) -> dict:
130
+ try:
131
+ if content_type == "text":
132
+ response = model.generate_content(
133
+ f"""As a retail shopping assistant, analyze this request and extract shopping context,
134
+ specific products needed, and any budget constraints. Request: {content}"""
135
+ )
136
+ else: # For images, documents, etc.
137
+ response = vision_model.generate_content(
138
+ [
139
+ "Analyze this content and extract shopping-related information, products needed, and any specifications.",
140
+ content
141
+ ]
142
+ )
143
+
144
+ analysis = response.text
145
+
146
+ # Second pass to find specific products
147
+ products_prompt = f"""Based on this analysis: {analysis}
148
+ Find matching products from our inventory considering these details:
149
+ {json.dumps(self.product_data)}
150
+ Return only product names and prices that match the request."""
151
+
152
+ products_response = model.generate_content(products_prompt)
153
+ return self.format_recommendations(products_response.text, budget)
154
+
155
+ except Exception as e:
156
+ logger.error(f"Error in content analysis: {str(e)}")
157
+ return {"error": "Failed to analyze content"}
158
+
159
+ def format_recommendations(self, recommendations: str, budget: float = None) -> dict:
160
+ try:
161
+ # Parse recommendations and filter by budget if specified
162
+ suggested_products = []
163
+ total = 0
164
+
165
+ for product in self.product_data:
166
+ if product['product'].lower() in recommendations.lower():
167
+ if budget and (total + product['price']) > budget:
168
+ continue
169
+ suggested_products.append(product)
170
+ total += product['price']
171
+
172
+ return {
173
+ "products": suggested_products,
174
+ "total": total,
175
+ "message": "No products found" if not suggested_products else "Products found"
176
+ }
177
+ except Exception as e:
178
+ logger.error(f"Error formatting recommendations: {str(e)}")
179
+ return {"error": "Failed to format recommendations"}
180
+
181
+ # Initialize WhatsApp and DNS setup (keeping your original configuration)
182
+
183
  @app.post("/")
184
  def hook():
185
  """Main webhook handler"""
186
  try:
187
  data = request.get_json()
188
+ logger.info("Received webhook data: %s", data)
189
+
190
+ changed_field = messenger.changed_field(data)
191
+ if changed_field != "messages":
192
+ return "OK", 200
193
+
194
+ if not messenger.is_message(data):
195
+ delivery = messenger.get_delivery(data)
196
+ if delivery:
197
+ logger.info(f"Message delivery status: {delivery}")
198
+ return "OK", 200
199
+
200
  mobile = messenger.get_mobile(data)
201
+ name = messenger.get_name(data)
202
+ message_type = messenger.get_message_type(data)
203
 
204
+ shopping_assistant = ShoppingAssistant()
205
+
206
+ if message_type == "text":
207
  message = messenger.get_message(data)
208
+ result = shopping_assistant.analyze_content(message, "text")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
 
210
+ if "error" in result:
211
+ messenger.send_message(f"Sorry, I couldn't process your request.", mobile)
212
+ return "OK", 200
213
+
214
+ if not result["products"]:
215
+ messenger.send_message("I couldn't find any matching products.", mobile)
216
+ return "OK", 200
217
+
218
+ # Format product message
219
+ products_text = "Here are the suggested products:\n\n"
220
+ for product in result["products"]:
221
+ products_text += f"- {product['product']}: ${product['price']}\n"
222
+ products_text += f"\nTotal: ${result['total']:.2f}"
223
+
224
+ # Send interactive message with buttons
225
+ buttons = [
226
+ {"id": "add_to_cart", "title": "Add to Cart"},
227
+ {"id": "cancel", "title": "Cancel"}
228
+ ]
229
+ messenger.send_reply_button(products_text, buttons, mobile)
230
+
231
+ elif message_type == "interactive":
232
+ handle_interactive_message(messenger, data, mobile, carts)
233
+
234
+ elif message_type in ["image", "document", "audio"]:
235
+ media_content = handle_media_message(messenger, data, message_type, mobile)
236
+ if media_content:
237
+ result = shopping_assistant.analyze_content(media_content, message_type)
238
+ # Handle result similar to text message
239
+
240
  return "OK", 200
241
 
242
  except Exception as e:
243
+ logger.error(f"Error in webhook handler: {str(e)}")
244
  return "Internal server error", 500
245
 
246
+ def handle_interactive_message(messenger, data, mobile, carts):
247
+ """Handle interactive message responses"""
248
+ try:
249
+ response = messenger.get_interactive_response(data)
250
+ button_id = response.get("id")
 
 
 
 
 
 
 
 
 
 
 
 
251
 
252
+ if button_id == "add_to_cart":
253
+ # Add products to cart
254
+ if mobile not in carts:
255
+ carts[mobile] = []
256
+ # Add current products to cart logic here
257
+
258
+ buttons = [
259
+ {"id": "more_shopping", "title": "Do More Shopping"},
260
+ {"id": "checkout", "title": "Checkout"}
261
+ ]
262
+ messenger.send_reply_button("Products added to cart!", buttons, mobile)
263
+
264
+ elif button_id == "checkout":
265
+ if mobile not in carts or not carts[mobile]:
266
+ messenger.send_message("Your cart is empty!", mobile)
267
+ return
268
+
269
+ buttons = [
270
+ {"id": "confirm_checkout", "title": "Confirm"},
271
+ {"id": "cancel_checkout", "title": "Cancel"}
272
+ ]
273
+ messenger.send_reply_button("Ready to complete your purchase?", buttons, mobile)
274
+
275
+ elif button_id == "confirm_checkout":
276
+ # Process checkout
277
+ carts[mobile] = [] # Clear cart
278
+ messenger.send_message("Thank you for your purchase!", mobile)
279
+
280
+ except Exception as e:
281
+ logger.error(f"Error handling interactive message: {str(e)}")
282
+ messenger.send_message("Sorry, something went wrong.", mobile)
283
+
284
+ # Keep your original media handling, DNS setup, and other utility functions
285
 
286
  def test_connection():
287
  """Test DNS resolution and connection to Graph API"""
 
298
  logger.error(f"Connection test failed: {e}")
299
 
300
  if __name__ == "__main__":
 
301
  test_connection()
 
 
302
  app.run(debug=True, host="0.0.0.0", port=7860)