rairo commited on
Commit
81121a5
Β·
verified Β·
1 Parent(s): c73c783

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +173 -356
main.py CHANGED
@@ -1,6 +1,7 @@
1
  import os
2
  import logging
3
  import socket
 
4
  import requests
5
  import dns.resolver
6
  import pandas as pd
@@ -11,11 +12,10 @@ from flask import Flask, request, make_response
11
  from typing import Dict, List, Union
12
  import json
13
  from PIL import Image
 
14
  # Initialize Flask App
15
  app = Flask(__name__)
16
 
17
-
18
-
19
  # Configure logging
20
  logging.basicConfig(
21
  level=logging.INFO,
@@ -23,24 +23,21 @@ logging.basicConfig(
23
  )
24
  logger = logging.getLogger(__name__)
25
 
26
- # Load .env file
27
  load_dotenv()
28
 
29
- # Configure DNS resolver with environment variables from Spaces
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"""
36
  resolver = dns.resolver.Resolver()
37
  resolver.nameservers = [nameserver1, nameserver2]
38
 
39
- # Create a custom getaddrinfo function
40
  _orig_getaddrinfo = socket.getaddrinfo
41
  def new_getaddrinfo(*args, **kwargs):
42
  try:
43
- # If the host is graph.facebook.com, use our custom resolver
44
  if args and args[0] == 'graph.facebook.com':
45
  answers = resolver.resolve('graph.facebook.com', 'A')
46
  ip = str(answers[0])
@@ -49,10 +46,8 @@ def setup_dns():
49
  logger.error(f"DNS resolution failed: {e}")
50
  return _orig_getaddrinfo(*args, **kwargs)
51
 
52
- # Replace the global getaddrinfo function
53
  socket.getaddrinfo = new_getaddrinfo
54
 
55
- # Setup DNS resolution
56
  setup_dns()
57
 
58
  # Initialize WhatsApp
@@ -61,136 +56,18 @@ try:
61
  os.getenv("whatsapp_token"),
62
  phone_number_id=os.getenv("phone_number_id")
63
  )
64
- if not os.getenv("whatsapp_token") or not os.getenv("phone_number_id"):
65
- raise ValueError("Missing required environment variables")
66
  except Exception as e:
67
- logger.error(f"Failed to initialize WhatsApp messenger: {str(e)}")
68
  raise
69
 
70
  VERIFY_TOKEN = "30cca545-3838-48b2-80a7-9e43b1ae8ce4"
71
 
72
- @app.get("/health")
73
- def health_check():
74
- """Health check endpoint to verify DNS resolution"""
75
- try:
76
- resolver = dns.resolver.Resolver()
77
- resolver.nameservers = [nameserver1, nameserver2]
78
- ip = resolver.resolve('graph.facebook.com', 'A')[0]
79
- return {
80
- "status": "healthy",
81
- "dns_resolution": str(ip),
82
- "nameserver1": nameserver1,
83
- "nameserver2": nameserver2
84
- }
85
- except Exception as e:
86
- return {
87
- "status": "unhealthy",
88
- "error": str(e)
89
- }
90
-
91
-
92
- def create_reply_button_message(text: str, buttons: List[Dict]) -> Dict:
93
- """Create a reply button message format compatible with WhatsApp API"""
94
- return {
95
- "type": "button",
96
- "body": {
97
- "text": text
98
- },
99
- "action": {
100
- "buttons": [
101
- {
102
- "type": "reply",
103
- "reply": {
104
- "id": btn["id"],
105
- "title": btn["title"]
106
- }
107
- }
108
- for btn in buttons
109
- ]
110
- }
111
- }
112
-
113
-
114
- def handle_media_message(messenger, data, message_type, mobile):
115
- """
116
- Handle media messages (images, documents, audio)
117
- Retrieve and process media content
118
- """
119
- try:
120
- # Download media content based on type
121
- if message_type == "image":
122
- media_url = messenger.get_image_url(data)
123
- elif message_type == "document":
124
- media_url = messenger.get_document_url(data)
125
- elif message_type == "audio":
126
- media_url = messenger.get_audio_url(data)
127
- else:
128
- logger.error(f"Unsupported media type: {message_type}")
129
- return None
130
-
131
- # Download media content
132
- response = requests.get(media_url, headers={
133
- "Authorization": f"Bearer {os.getenv('whatsapp_token')}"
134
- })
135
-
136
- if response.status_code != 200:
137
- logger.error(f"Failed to download media: {response.status_code}")
138
- messenger.send_message("Sorry, I couldn't process your media.", mobile)
139
- return None
140
-
141
- # Ensure image is in a compatible format
142
- try:
143
- img = Image.open(io.BytesIO(response.content))
144
-
145
- # Convert to RGB if needed
146
- if img.mode != 'RGB':
147
- img = img.convert('RGB')
148
-
149
- # Resize large images to reduce processing time
150
- max_size = (1024, 1024)
151
- img.thumbnail(max_size)
152
-
153
- # Save to a BytesIO object
154
- img_byte_arr = io.BytesIO()
155
- img.save(img_byte_arr, format='JPEG')
156
- img_byte_arr = img_byte_arr.getvalue()
157
-
158
- return img_byte_arr
159
- except Exception as img_error:
160
- logger.error(f"Image processing error: {img_error}")
161
- messenger.send_message("Sorry, I had trouble processing the image.", mobile)
162
- return None
163
-
164
- except Exception as e:
165
- logger.error(f"Error handling media message: {str(e)}")
166
- messenger.send_message("Sorry, I couldn't process your media.", mobile)
167
- return None
168
-
169
-
170
- @app.get("/")
171
- def verify_token():
172
- """Webhook verification endpoint"""
173
- try:
174
- if request.args.get("hub.verify_token") == VERIFY_TOKEN:
175
- logger.info("Verified webhook")
176
- response = make_response(request.args.get("hub.challenge"), 200)
177
- response.mimetype = "text/plain"
178
- return response
179
- logger.error("Webhook Verification failed")
180
- return "Invalid verification token"
181
- except Exception as e:
182
- logger.error(f"Error in verify_token: {str(e)}")
183
- return "Internal server error", 500
184
-
185
- # Load .env file
186
- load_dotenv()
187
-
188
- # Initialize Gemini
189
  genai.configure(api_key=os.getenv("google_api_key"))
190
- model = genai.GenerativeModel('gemini-2.0-flash-exp')
191
  vision_model = genai.GenerativeModel('gemini-2.0-flash-exp')
 
192
 
193
- # Load product data
194
  try:
195
  df = pd.read_csv("supermarket_data1.csv")
196
  product_data = df.to_dict('records')
@@ -198,264 +75,204 @@ except Exception as e:
198
  logger.error(f"Failed to load product data: {str(e)}")
199
  raise
200
 
201
- # Cart management
202
  carts: Dict[str, List[Dict]] = {}
203
 
204
  class ShoppingAssistant:
205
  def __init__(self):
206
  self.product_data = product_data
207
  self.last_analyzed_products = {}
208
-
209
- def analyze_content(self, content: Union[str, bytes], content_type: str, mobile: str, budget: float = None) -> dict:
 
210
  try:
211
  if content_type == "text":
212
- response = model.generate_content(
213
- f"""As a retail shopping assistant, analyze this request and extract shopping context,
214
- specific products needed, pick only one item per product extracted
215
- unless the user specifies the amount or number of items for a particular product.
216
- If the user specifies budget make sure your suggest is within that budget.
217
-
218
- If a user suggest a scenario planning a dinner, planning a party, I want to clean my car etc, be analytic and creative
219
- and suggest appropriate product
220
- Request: {content}"""
221
- )
222
- else: # For images, documents, etc.
223
- response = vision_model.generate_content(content)
224
-
225
- analysis = response.text
226
- logger.info(f"Image analysis result: {analysis}")
227
-
228
- # Second pass to find specific products
229
- products_prompt = f"""Based on this analysis: {analysis}
230
- Find matching products from our inventory considering these details:
231
- {json.dumps(self.product_data)}
232
- Return only product names and prices that match the request."""
233
-
234
- products_response = model.generate_content(products_prompt)
235
- logger.info(f"Products response: {products_response.text}")
236
-
237
- result = self.format_recommendations(products_response.text, budget)
238
-
239
- # Store last analyzed products for this mobile number
240
- if mobile:
241
- self.last_analyzed_products[mobile] = result["products"]
242
-
243
- return result
244
-
245
  except Exception as e:
246
- logger.error(f"Error in content analysis: {str(e)}")
247
- return {"error": "Failed to analyze content"}
248
-
249
- def format_recommendations(self, recommendations: str, budget: float = None) -> dict:
250
- try:
251
- # Parse recommendations and filter by budget if specified
252
- suggested_products = []
253
- total = 0
254
-
255
- for product in self.product_data:
256
- if product['product'].lower() in recommendations.lower():
257
- if budget and (total + product['price']) > budget:
258
- continue
259
- suggested_products.append(product)
260
- total += product['price']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
 
262
- return {
263
- "products": suggested_products,
264
- "total": total,
265
- "message": "No products found" if not suggested_products else "Products found"
266
- }
267
- except Exception as e:
268
- logger.error(f"Error formatting recommendations: {str(e)}")
269
- return {"error": "Failed to format recommendations"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
 
271
- # Initialize WhatsApp and DNS setup (keeping your original configuration)
 
 
 
 
 
272
 
273
  @app.post("/")
274
- def hook():
275
  """Main webhook handler"""
276
  try:
277
  data = request.get_json()
278
- logger.info("Received webhook data: %s", data)
279
-
280
- changed_field = messenger.changed_field(data)
281
- if changed_field != "messages":
282
- return "OK", 200
283
-
284
- if not messenger.is_message(data):
285
- delivery = messenger.get_delivery(data)
286
- if delivery:
287
- logger.info(f"Message delivery status: {delivery}")
288
- return "OK", 200
289
-
290
  mobile = messenger.get_mobile(data)
291
- name = messenger.get_name(data)
292
  message_type = messenger.get_message_type(data)
293
-
294
- shopping_assistant = ShoppingAssistant()
295
 
 
296
  if message_type == "text":
297
  message = messenger.get_message(data)
298
- result = shopping_assistant.analyze_content(message, "text", mobile)
299
 
300
- if "error" in result:
301
- messenger.send_message("Sorry, I couldn't process your request.", mobile)
302
- return "OK", 200
303
-
304
- if not result["products"]:
305
- messenger.send_message("I couldn't find any matching products.", mobile)
306
- return "OK", 200
307
-
308
- # Format product message
309
- products_text = "Here are the suggested products:\n\n"
310
- for product in result["products"]:
311
- products_text += f"- {product['product']}: ${product['price']}\n"
312
- products_text += f"\nTotal: ${result['total']:.2f}"
313
-
314
- # Send interactive message with buttons
315
- buttons = [
316
- {"id": "add_to_cart", "title": "Add to Cart"},
317
- {"id": "cancel", "title": "Cancel"}
318
- ]
319
- button_message = create_reply_button_message(products_text, buttons)
320
- messenger.send_reply_button(
321
- recipient_id=mobile,
322
- button=button_message
323
- )
324
-
325
- elif message_type == "interactive":
326
- handle_interactive_message(messenger, data, mobile, carts, shopping_assistant)
327
-
328
- elif message_type in ["image", "document", "audio"]:
329
- media_content = handle_media_message(messenger, data, message_type, mobile)
330
- if media_content:
331
- result = shopping_assistant.analyze_content(media_content, message_type, mobile)
332
 
333
- # Check if products were found
334
- if result.get("products"):
335
- # Format product message
336
- products_text = "Here are the suggested products:\n\n"
337
- for product in result["products"]:
338
- products_text += f"- {product['product']}: ${product['price']}\n"
339
- products_text += f"\nTotal: ${result['total']:.2f}"
340
-
341
- # Create and send reply buttons
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
  buttons = [
343
- {"id": "add_to_cart", "title": "Add to Cart"},
344
- {"id": "cancel", "title": "Cancel"}
345
  ]
346
- button_message = create_reply_button_message(products_text, buttons)
347
  messenger.send_reply_button(
348
- recipient_id=mobile,
349
- button=button_message
350
  )
351
  else:
352
- messenger.send_message("I couldn't find any matching products in the image.", mobile)
353
-
354
- return "OK", 200
355
-
356
- except Exception as e:
357
- logger.error(f"Error in webhook handler: {str(e)}")
358
- return "Internal server error", 500
359
-
360
- def handle_interactive_message(messenger, data, mobile, carts, shopping_assistant):
361
- """Handle interactive message responses with enhanced cart functionality"""
362
- try:
363
- response = messenger.get_interactive_response(data)
364
- button_id = response.get("id")
365
-
366
- if button_id == "add_to_cart":
367
- # Add last analyzed products to cart
368
- if mobile not in carts:
369
- carts[mobile] = []
370
-
371
- # Add products from last analysis
372
- if mobile in shopping_assistant.last_analyzed_products:
373
- carts[mobile].extend(shopping_assistant.last_analyzed_products[mobile])
374
- del shopping_assistant.last_analyzed_products[mobile]
375
-
376
- buttons = [
377
- {"id": "continue_shopping", "title": "Continue Shopping"},
378
- {"id": "checkout", "title": "Checkout"}
379
- ]
380
- button_message = create_reply_button_message(
381
- "Products added to cart! What would you like to do next?",
382
- buttons
383
- )
384
- messenger.send_reply_button(
385
- recipient_id=mobile,
386
- button=button_message
387
- )
388
-
389
- elif button_id == "continue_shopping":
390
- # Encourage further shopping
391
- messenger.send_message(
392
- "Great! You can continue shopping by sending me a text or image of what you're looking for.",
393
- mobile
394
- )
395
-
396
- elif button_id == "checkout":
397
- if mobile not in carts or not carts[mobile]:
398
- messenger.send_message("Your cart is empty!", mobile)
399
- return
400
 
401
- # Calculate total
402
- total = sum(product['price'] for product in carts[mobile])
 
 
403
 
404
- # Generate receipt
405
- receipt_text = "Your Purchase Receipt:\n\n"
406
- for product in carts[mobile]:
407
- receipt_text += f"- {product['product']}: ${product['price']:.2f}\n"
408
- receipt_text += f"\nTotal: ${total:.2f}\n\n"
409
- receipt_text += "Expected Delivery: Within 1 hour\n"
410
- receipt_text += "Thank you for your purchase!"
411
-
412
- buttons = [
413
- {"id": "confirm_order", "title": "Confirm Order"},
414
- {"id": "modify_order", "title": "Modify Order"}
415
- ]
416
- button_message = create_reply_button_message(receipt_text, buttons)
417
- messenger.send_reply_button(
418
- recipient_id=mobile,
419
- button=button_message
420
- )
 
 
421
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
422
 
423
- elif button_id == "confirm_order":
424
- # Finalize order
425
- messenger.send_message(
426
- "Order confirmed! Your items will be delivered within 1 hour. Thank you!",
427
- mobile
428
- )
429
- # Clear the cart
430
- carts[mobile] = []
431
-
432
- elif button_id == "modify_order":
433
- # Allow user to modify order
434
- messenger.send_message(
435
- "Please tell us any changes you'd like to make to your order.",
436
- mobile
437
- )
438
-
439
- except Exception as e:
440
- logger.error(f"Error handling interactive message: {str(e)}")
441
- messenger.send_message("Sorry, something went wrong.", mobile)
442
-
443
-
444
 
445
- def test_connection():
446
- """Test DNS resolution and connection to Graph API"""
447
- try:
448
- resolver = dns.resolver.Resolver()
449
- resolver.nameservers = [nameserver1, nameserver2]
450
- ip = resolver.resolve('graph.facebook.com', 'A')[0]
451
- logger.info(f"DNS Resolution successful: {ip}")
452
-
453
- response = requests.get('https://graph.facebook.com')
454
- logger.info(f"HTTPS Connection successful: {response.status_code}")
455
-
456
  except Exception as e:
457
- logger.error(f"Connection test failed: {e}")
 
458
 
459
  if __name__ == "__main__":
460
- test_connection()
461
- app.run(debug=True, host="0.0.0.0", port=7860)
 
1
  import os
2
  import logging
3
  import socket
4
+ import io
5
  import requests
6
  import dns.resolver
7
  import pandas as pd
 
12
  from typing import Dict, List, Union
13
  import json
14
  from PIL import Image
15
+
16
  # Initialize Flask App
17
  app = Flask(__name__)
18
 
 
 
19
  # Configure logging
20
  logging.basicConfig(
21
  level=logging.INFO,
 
23
  )
24
  logger = logging.getLogger(__name__)
25
 
26
+ # Load environment variables
27
  load_dotenv()
28
 
29
+ # DNS Configuration
30
  nameserver1 = os.getenv('nameserver1', '8.8.8.8')
31
  nameserver2 = os.getenv('nameserver2', '8.8.4.4')
32
 
 
33
  def setup_dns():
34
  """Configure DNS resolution globally"""
35
  resolver = dns.resolver.Resolver()
36
  resolver.nameservers = [nameserver1, nameserver2]
37
 
 
38
  _orig_getaddrinfo = socket.getaddrinfo
39
  def new_getaddrinfo(*args, **kwargs):
40
  try:
 
41
  if args and args[0] == 'graph.facebook.com':
42
  answers = resolver.resolve('graph.facebook.com', 'A')
43
  ip = str(answers[0])
 
46
  logger.error(f"DNS resolution failed: {e}")
47
  return _orig_getaddrinfo(*args, **kwargs)
48
 
 
49
  socket.getaddrinfo = new_getaddrinfo
50
 
 
51
  setup_dns()
52
 
53
  # Initialize WhatsApp
 
56
  os.getenv("whatsapp_token"),
57
  phone_number_id=os.getenv("phone_number_id")
58
  )
 
 
59
  except Exception as e:
60
+ logger.error(f"Failed to initialize WhatsApp: {str(e)}")
61
  raise
62
 
63
  VERIFY_TOKEN = "30cca545-3838-48b2-80a7-9e43b1ae8ce4"
64
 
65
+ # Gemini Configuration
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  genai.configure(api_key=os.getenv("google_api_key"))
 
67
  vision_model = genai.GenerativeModel('gemini-2.0-flash-exp')
68
+ text_model = genai.GenerativeModel('gemini-2.0-flash-exp')
69
 
70
+ # Product Data and Cart
71
  try:
72
  df = pd.read_csv("supermarket_data1.csv")
73
  product_data = df.to_dict('records')
 
75
  logger.error(f"Failed to load product data: {str(e)}")
76
  raise
77
 
 
78
  carts: Dict[str, List[Dict]] = {}
79
 
80
  class ShoppingAssistant:
81
  def __init__(self):
82
  self.product_data = product_data
83
  self.last_analyzed_products = {}
84
+
85
+ def process_input(self, content: Union[str, bytes], content_type: str, mobile: str) -> dict:
86
+ """Multimodal input processor"""
87
  try:
88
  if content_type == "text":
89
+ return self._process_text(content, mobile)
90
+ elif content_type == "image":
91
+ return self._process_image(content, mobile)
92
+ else:
93
+ return {"error": "Unsupported content type"}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  except Exception as e:
95
+ logger.error(f"Processing error: {str(e)}")
96
+ return {"error": "Failed to process input"}
97
+
98
+ def _process_text(self, text: str, mobile: str) -> dict:
99
+ """Process text input using Gemini"""
100
+ response = text_model.generate_content(
101
+ f"""Analyze this shopping request: {text}
102
+ Identify products needed, quantities, and special requirements.
103
+ Respond with JSON format: {{products: [{{name: '', quantity: 1}}]}}"""
104
+ )
105
+ analysis = json.loads(response.text)
106
+ return self._match_products(analysis.get('products', []), mobile)
107
+
108
+ def _process_image(self, image_bytes: bytes, mobile: str) -> dict:
109
+ """Process image input using Gemini Vision"""
110
+ img = Image.open(io.BytesIO(image_bytes))
111
+ response = vision_model.generate_content([
112
+ "Analyze this shopping-related image and identify products",
113
+ img
114
+ ])
115
+ analysis = json.loads(response.text)
116
+ return self._match_products(analysis.get('products', []), mobile)
117
+
118
+ def _match_products(self, detected_items: List[dict], mobile: str) -> dict:
119
+ """Match detected items with inventory"""
120
+ matched_products = []
121
+ for item in detected_items:
122
+ product = next(
123
+ (p for p in self.product_data
124
+ if p['product'].lower() == item['name'].lower()),
125
+ None
126
+ )
127
+ if product:
128
+ matched_products.append({
129
+ **product,
130
+ 'quantity': item.get('quantity', 1)
131
+ })
132
+
133
+ if mobile:
134
+ self.last_analyzed_products[mobile] = matched_products
135
 
136
+ return {
137
+ "products": matched_products,
138
+ "total": sum(p['price']*p.get('quantity',1) for p in matched_products),
139
+ "message": f"Found {len(matched_products)} matching products"
140
+ }
141
+
142
+ def create_reply_button_message(text: str, buttons: List[Dict]) -> Dict:
143
+ """Create interactive button message"""
144
+ return {
145
+ "type": "button",
146
+ "body": {"text": text},
147
+ "action": {
148
+ "buttons": [
149
+ {
150
+ "type": "reply",
151
+ "reply": {
152
+ "id": btn["id"],
153
+ "title": btn["title"]
154
+ }
155
+ } for btn in buttons
156
+ ]
157
+ }
158
+ }
159
 
160
+ @app.get("/")
161
+ def verify_token():
162
+ """Webhook verification"""
163
+ if request.args.get("hub.verify_token") == VERIFY_TOKEN:
164
+ return request.args.get("hub.challenge")
165
+ return "Invalid verification token"
166
 
167
  @app.post("/")
168
+ def webhook():
169
  """Main webhook handler"""
170
  try:
171
  data = request.get_json()
 
 
 
 
 
 
 
 
 
 
 
 
172
  mobile = messenger.get_mobile(data)
 
173
  message_type = messenger.get_message_type(data)
174
+ assistant = ShoppingAssistant()
 
175
 
176
+ # Handle text input
177
  if message_type == "text":
178
  message = messenger.get_message(data)
179
+ result = assistant.process_input(message, "text", mobile)
180
 
181
+ if result.get('products'):
182
+ products_text = "πŸ›’ *Suggested Products:*\n\n"
183
+ for p in result['products']:
184
+ products_text += f"➀ {p['product']} x{p.get('quantity',1)}\n Price: ${p['price']}\n\n"
185
+ products_text += f"πŸ’΅ Total: ${result['total']:.2f}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
 
187
+ buttons = [
188
+ {"id": "add_to_cart", "title": "πŸ›’ Add to Cart"},
189
+ {"id": "cancel", "title": "❌ Cancel"}
190
+ ]
191
+ messenger.send_reply_button(
192
+ mobile,
193
+ create_reply_button_message(products_text, buttons)
194
+ )
195
+ else:
196
+ messenger.send_message("❌ No matching products found", mobile)
197
+
198
+ # Handle image input
199
+ elif message_type == "image":
200
+ image_url = messenger.get_image_url(data)
201
+ response = requests.get(image_url, headers={
202
+ "Authorization": f"Bearer {os.getenv('whatsapp_token')}"
203
+ })
204
+
205
+ if response.status_code == 200:
206
+ result = assistant.process_input(response.content, "image", mobile)
207
+
208
+ if result.get('products'):
209
+ products_text = "πŸ“Έ *Products Found in Your Image:*\n\n"
210
+ for p in result['products']:
211
+ products_text += f"➀ {p['product']} x{p.get('quantity',1)}\n Price: ${p['price']}\n\n"
212
+ products_text += f"πŸ’΅ Total: ${result['total']:.2f}"
213
+
214
  buttons = [
215
+ {"id": "add_to_cart", "title": "πŸ›’ Add to Cart"},
216
+ {"id": "cancel", "title": "❌ Cancel"}
217
  ]
 
218
  messenger.send_reply_button(
219
+ mobile,
220
+ create_reply_button_message(products_text, buttons)
221
  )
222
  else:
223
+ messenger.send_message("❌ No products found in the image", mobile)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
 
225
+ # Handle button interactions
226
+ elif message_type == "interactive":
227
+ response = messenger.get_interactive_response(data)
228
+ button_id = response.get("id")
229
 
230
+ if button_id == "add_to_cart":
231
+ if mobile in assistant.last_analyzed_products:
232
+ if mobile not in carts:
233
+ carts[mobile] = []
234
+ carts[mobile].extend(assistant.last_analyzed_products[mobile])
235
+
236
+ buttons = [
237
+ {"id": "continue_shopping", "title": "πŸ›οΈ Continue Shopping"},
238
+ {"id": "checkout", "title": "πŸ’³ Checkout"}
239
+ ]
240
+ messenger.send_reply_button(
241
+ mobile,
242
+ create_reply_button_message(
243
+ "βœ… Items added to cart!\nWhat would you like to do next?",
244
+ buttons
245
+ )
246
+ )
247
+ else:
248
+ messenger.send_message("❌ No products to add", mobile)
249
 
250
+ elif button_id == "checkout":
251
+ if mobile in carts and carts[mobile]:
252
+ total = sum(p['price']*p.get('quantity',1) for p in carts[mobile])
253
+ receipt = "πŸ“‹ *Your Cart Summary:*\n\n"
254
+ receipt += "\n".join(
255
+ f"➀ {p['product']} x{p.get('quantity',1)} - ${p['price']}"
256
+ for p in carts[mobile]
257
+ )
258
+ receipt += f"\n\nπŸ’³ Total: ${total:.2f}"
259
+
260
+ buttons = [
261
+ {"id": "confirm_checkout", "title": "βœ… Confirm Order"},
262
+ {"id": "edit_cart", "title": "✏️ Edit Cart"}
263
+ ]
264
+ messenger.send_reply_button(
265
+ mobile,
266
+ create_reply_button_message(receipt, buttons)
267
+ )
268
+ else:
269
+ messenger.send_message("πŸ›’ Your cart is empty!", mobile)
270
 
271
+ return "OK", 200
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
272
 
 
 
 
 
 
 
 
 
 
 
 
273
  except Exception as e:
274
+ logger.error(f"Webhook error: {str(e)}")
275
+ return "Internal Server Error", 500
276
 
277
  if __name__ == "__main__":
278
+ app.run(host="0.0.0.0", port=7860)