DreamStream-1 commited on
Commit
1d946f7
·
verified ·
1 Parent(s): 1ed5753

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +404 -70
app.py CHANGED
@@ -888,66 +888,154 @@ def split_message_for_whatsapp(message: str, max_length: int = 1000) -> list:
888
  return [message[i:i+max_length] for i in range(0, len(message), max_length)]
889
 
890
  def send_whatsjet_message(phone_number: str, message: str, media_type: str = None, media_path: str = None, filename: str = None) -> bool:
891
- """Send a message using WhatsJet API with optional media attachment"""
892
  if not all([WHATSJET_API_URL, WHATSJET_VENDOR_UID, WHATSJET_API_TOKEN]):
893
  logger.error("[WhatsJet] Missing environment variables.")
894
  return False
895
 
896
  url = f"{WHATSJET_API_URL}/{WHATSJET_VENDOR_UID}/contact/send-message?token={WHATSJET_API_TOKEN}"
897
 
898
- # Handle media messages
899
- if media_type and media_path:
900
  try:
901
- with open(media_path, 'rb') as f:
902
- media_content = f.read()
903
- media_b64 = base64.b64encode(media_content).decode('utf-8')
 
 
 
 
 
904
  payload = {
905
- "phone_number": phone_number,
906
- "message_body": message,
907
- 'media_type': media_type,
908
- 'media_content': media_b64,
909
- 'media_filename': filename or os.path.basename(media_path)
910
  }
911
- # Send message with increased timeout
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
912
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
913
  response = httpx.post(
914
  url,
915
  json=payload,
916
- timeout=15
917
  )
 
 
 
 
 
 
 
918
  response.raise_for_status()
919
- logger.info(f"[WhatsJet] Media message sent successfully to {phone_number}")
920
  return True
921
  except Exception as e:
922
- logger.error(f"[WhatsJet] Exception sending media message: {e}")
 
923
  return False
924
  except Exception as e:
925
- logger.error(f"[WhatsJet] Exception preparing media message: {str(e)}")
 
926
  return False
927
 
928
- # Handle text messages
929
  if not message.strip():
930
  return True # Don't send empty messages
931
 
932
  for chunk in split_message_for_whatsapp(message):
933
  try:
934
  payload = {"phone_number": phone_number, "message_body": chunk}
935
- # Send message with increased timeout
936
- try:
937
- response = httpx.post(
938
- url,
939
- json=payload,
940
- timeout=15
941
- )
942
- response.raise_for_status()
943
- logger.info(f"[WhatsJet] Text chunk sent successfully to {phone_number}")
944
- except Exception as e:
945
- logger.error(f"[WhatsJet] Exception sending text chunk: {e}")
946
- return False
 
947
  except Exception as e:
948
- logger.error(f"[WhatsJet] Exception preparing text chunk: {str(e)}")
 
949
  return False
950
-
951
  logger.info(f"[WhatsJet] Successfully sent complete text message to {phone_number}")
952
  return True
953
 
@@ -3366,35 +3454,38 @@ load_products_data()
3366
 
3367
  def get_product_image_path(product_name: str) -> str:
3368
  """
3369
- Get the image path for a product
3370
- Returns the path to the product image if it exists, otherwise None
3371
  """
3372
  try:
3373
- # Create images directory if it doesn't exist
 
 
 
 
 
 
 
 
 
 
 
 
3374
  images_dir = "static/images"
3375
  os.makedirs(images_dir, exist_ok=True)
3376
-
3377
- # Clean product name for filename
3378
  safe_name = re.sub(r'[^\w\s-]', '', product_name).replace(' ', '_').lower()
3379
-
3380
- # Check for common image extensions
3381
  image_extensions = ['.jpg', '.jpeg', '.png', '.webp', '.gif']
3382
-
3383
  for ext in image_extensions:
3384
  image_path = os.path.join(images_dir, f"{safe_name}{ext}")
3385
  if os.path.exists(image_path):
3386
  logger.info(f"[Image] Found product image: {image_path}")
3387
  return image_path
3388
-
3389
- # If no specific product image found, check for a default image
3390
  default_image_path = os.path.join(images_dir, "default_product.jpg")
3391
  if os.path.exists(default_image_path):
3392
  logger.info(f"[Image] Using default product image: {default_image_path}")
3393
  return default_image_path
3394
-
3395
  logger.warning(f"[Image] No image found for product: {product_name}")
3396
  return None
3397
-
3398
  except Exception as e:
3399
  logger.error(f"[Image] Error getting product image path: {e}")
3400
  return None
@@ -3576,11 +3667,20 @@ async def send_product_image_with_caption(from_number: str, product: Dict[str, A
3576
  product_name = product.get('Product Name', 'Unknown Product')
3577
  details = generate_veterinary_product_response(product, user_context)
3578
  image_url = product.get('Images', '').strip() if 'Images' in product else ''
3579
- temp_file_path = None
 
 
 
 
 
 
 
3580
  try:
 
3581
  if image_url:
3582
  # Convert Google Drive link to direct download if needed
3583
  if 'drive.google.com' in image_url:
 
3584
  if '/d/' in image_url:
3585
  file_id = image_url.split('/d/')[1].split('/')[0]
3586
  elif 'id=' in image_url:
@@ -3589,58 +3689,97 @@ async def send_product_image_with_caption(from_number: str, product: Dict[str, A
3589
  file_id = ''
3590
  if file_id:
3591
  image_url = f"https://drive.google.com/uc?export=download&id={file_id}"
3592
- # Download the image to a temp file
3593
- response = requests.get(image_url, stream=True, timeout=10)
3594
- if response.status_code == 200:
3595
- with tempfile.NamedTemporaryFile(delete=False, suffix='.jpg') as tmp:
3596
- for chunk in response.iter_content(1024):
3597
- tmp.write(chunk)
3598
- temp_file_path = tmp.name
3599
- media_type = 'image/jpeg'
3600
- filename = f"{product_name.replace(' ', '_')}.jpg"
 
 
 
 
 
 
 
 
 
 
 
 
3601
  success = send_whatsjet_message(
3602
  from_number,
3603
  details,
3604
  media_type=media_type,
3605
- media_path=temp_file_path,
3606
  filename=filename
3607
  )
 
3608
  if success:
3609
- logger.info(f"[Product] Sent image from CSV link with caption for product: {product_name}")
3610
  return
3611
  else:
3612
- logger.warning(f"[Product] Failed to send image from CSV link, sending text only: {product_name}")
3613
- else:
3614
- logger.warning(f"[Product] Could not download image from CSV link: {image_url}")
3615
- # Fallback to local static/images
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3616
  image_path = get_product_image_path(product_name)
3617
- if image_path and os.path.exists(image_path):
3618
  media_type = get_product_image_media_type(image_path)
3619
  filename = f"{product_name.replace(' ', '_')}.jpg"
 
 
 
 
 
 
 
 
 
 
3620
  success = send_whatsjet_message(
3621
  from_number,
3622
  details,
3623
  media_type=media_type,
3624
- media_path=image_path,
3625
  filename=filename
3626
  )
 
3627
  if success:
3628
- logger.info(f"[Product] Sent image with caption for product: {product_name}")
3629
  else:
3630
  logger.warning(f"[Product] Failed to send image, sending text only: {product_name}")
3631
  send_whatsjet_message(from_number, details)
3632
  else:
 
 
3633
  send_whatsjet_message(from_number, details)
3634
- logger.info(f"[Product] Sent product info without image: {product_name}")
3635
  except Exception as e:
3636
  logger.error(f"[Product] Error sending product image with caption: {e}")
 
3637
  send_whatsjet_message(from_number, details)
3638
- finally:
3639
- if temp_file_path and os.path.exists(temp_file_path):
3640
- try:
3641
- os.remove(temp_file_path)
3642
- except Exception:
3643
- pass
3644
 
3645
  @app.get("/test-product-image-with-caption")
3646
  async def test_product_image_with_caption(phone: str):
@@ -3660,7 +3799,202 @@ async def test_product_image_with_caption(phone: str):
3660
  except Exception as e:
3661
  return {"error": str(e)}
3662
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3663
  if __name__ == "__main__":
3664
  # Launch FastAPI app
3665
  import uvicorn
3666
- uvicorn.run(app, host="0.0.0.0", port=7860)
 
888
  return [message[i:i+max_length] for i in range(0, len(message), max_length)]
889
 
890
  def send_whatsjet_message(phone_number: str, message: str, media_type: str = None, media_path: str = None, filename: str = None) -> bool:
891
+ """Send a message using WhatsJet API with optional media attachment. For images, send the image as a media message (no caption), then send the caption as a separate text message."""
892
  if not all([WHATSJET_API_URL, WHATSJET_VENDOR_UID, WHATSJET_API_TOKEN]):
893
  logger.error("[WhatsJet] Missing environment variables.")
894
  return False
895
 
896
  url = f"{WHATSJET_API_URL}/{WHATSJET_VENDOR_UID}/contact/send-message?token={WHATSJET_API_TOKEN}"
897
 
898
+ # For images: use /contact/send-media-message endpoint with media_url, media_type, caption, and file_name
899
+ if media_type in ["image/jpeg", "image/png"] and media_path:
900
  try:
901
+ # Use direct public URL for WhatsJet (media_url)
902
+ if media_path.startswith("http://") or media_path.startswith("https://"):
903
+ image_url = media_path
904
+ else:
905
+ # If local file, upload to a public location or return False (not supported here)
906
+ logger.error("[WhatsJet] Local file paths are not supported for media_url. Please provide a public URL.")
907
+ return False
908
+ media_api_url = f"{WHATSJET_API_URL}/{WHATSJET_VENDOR_UID}/contact/send-media-message"
909
  payload = {
910
+ "phone_number": phone_number,
911
+ "media_type": "image",
912
+ "media_url": image_url,
913
+ "caption": message,
914
+ "file_name": filename or "image.jpg"
915
  }
916
+ logger.info(f"[WhatsJet][DEBUG] Media API URL: {media_api_url}")
917
+ logger.info(f"[WhatsJet][DEBUG] Media payload: {payload}")
918
+ response = httpx.post(
919
+ media_api_url,
920
+ json=payload,
921
+ timeout=30
922
+ )
923
+ logger.info(f"[WhatsJet][DEBUG] Media response status: {response.status_code}")
924
+ logger.info(f"[WhatsJet][DEBUG] Media response headers: {dict(response.headers)}")
925
+ logger.info(f"[WhatsJet][DEBUG] Media response body: {response.text}")
926
+ response.raise_for_status()
927
+ logger.info(f"[WhatsJet] Media image sent successfully via media_url to {phone_number}")
928
+ return True
929
+ except Exception as e:
930
+ import traceback
931
+ logger.error(f"[WhatsJet][ERROR] Exception sending image via media_url: {str(e)}\nTraceback: {traceback.format_exc()}")
932
+ # Fallback: send text only
933
+ return send_whatsjet_message(phone_number, message)
934
+
935
+ # Handle other media messages (existing logic)
936
+ if media_type and media_path:
937
+ try:
938
+ if isinstance(media_path, str) and (media_path.startswith("http://") or media_path.startswith("https://")):
939
+ response = requests.get(media_path, stream=True, timeout=15)
940
+ response.raise_for_status()
941
+ media_content = response.content
942
+ logger.info(f"[WhatsJet][DEBUG] Downloaded media content-type: {response.headers.get('Content-Type')}")
943
+ logger.info(f"[WhatsJet][DEBUG] First 20 bytes: {media_content[:20]}")
944
+ else:
945
+ with open(media_path, 'rb') as f:
946
+ media_content = f.read()
947
+ # Try multipart first, then base64
948
  try:
949
+ files = {
950
+ 'media': (filename or 'file.bin', media_content, media_type)
951
+ }
952
+ data = {
953
+ 'phone_number': phone_number,
954
+ 'message_body': message
955
+ }
956
+ # Enhanced logging
957
+ logger.info(f"[WhatsJet][DEBUG] URL: {url}")
958
+ logger.info(f"[WhatsJet][DEBUG] Multipart data: {data}")
959
+ logger.info(f"[WhatsJet][DEBUG] Multipart files: {list(files.keys())}")
960
+ response = httpx.post(
961
+ url,
962
+ data=data,
963
+ files=files,
964
+ timeout=30
965
+ )
966
+ logger.info(f"[WhatsJet][DEBUG] Multipart response status: {response.status_code}")
967
+ logger.info(f"[WhatsJet][DEBUG] Multipart response headers: {dict(response.headers)}")
968
+ try:
969
+ response_text = response.text
970
+ logger.info(f"[WhatsJet][DEBUG] Multipart response body: {response_text[:1000]}" + ("..." if len(response_text) > 1000 else ""))
971
+ except Exception as e:
972
+ logger.info(f"[WhatsJet][DEBUG] Multipart response body: Unable to read: {e}")
973
+ response.raise_for_status()
974
+ logger.info(f"[WhatsJet] Media message sent successfully via multipart to {phone_number}")
975
+ return True
976
+ except Exception as e:
977
+ import traceback
978
+ logger.warning(f"[WhatsJet][ERROR] Multipart upload failed for media, trying base64: {e}\nTraceback: {traceback.format_exc()}")
979
+
980
+ media_b64 = base64.b64encode(media_content).decode('utf-8')
981
+ payload = {
982
+ "phone_number": phone_number,
983
+ "message_body": message,
984
+ 'media_type': media_type,
985
+ 'media_content': media_b64,
986
+ 'media_filename': filename or os.path.basename(media_path) if not media_path.startswith('http') else filename or 'file.bin'
987
+ }
988
+ # Enhanced logging
989
+ logger.info(f"[WhatsJet][DEBUG] URL: {url}")
990
+ logger.info(f"[WhatsJet][DEBUG] Base64 payload: {{'phone_number': payload['phone_number'], 'media_type': payload['media_type'], 'media_filename': payload['media_filename'], 'message_body': payload['message_body'][:50] + '...', 'media_content_length': len(payload['media_content'])}}")
991
  response = httpx.post(
992
  url,
993
  json=payload,
994
+ timeout=30
995
  )
996
+ logger.info(f"[WhatsJet][DEBUG] Base64 response status: {response.status_code}")
997
+ logger.info(f"[WhatsJet][DEBUG] Base64 response headers: {dict(response.headers)}")
998
+ try:
999
+ response_text = response.text
1000
+ logger.info(f"[WhatsJet][DEBUG] Base64 response body: {response_text[:1000]}" + ("..." if len(response_text) > 1000 else ""))
1001
+ except Exception as e:
1002
+ logger.info(f"[WhatsJet][DEBUG] Base64 response body: Unable to read: {e}")
1003
  response.raise_for_status()
1004
+ logger.info(f"[WhatsJet] Media message sent successfully via base64 to {phone_number}")
1005
  return True
1006
  except Exception as e:
1007
+ import traceback
1008
+ logger.error(f"[WhatsJet][ERROR] Exception sending media message: {e}\nTraceback: {traceback.format_exc()}")
1009
  return False
1010
  except Exception as e:
1011
+ import traceback
1012
+ logger.error(f"[WhatsJet][ERROR] Exception preparing media message: {str(e)}\nTraceback: {traceback.format_exc()}")
1013
  return False
1014
 
1015
+ # Handle text messages (existing logic)
1016
  if not message.strip():
1017
  return True # Don't send empty messages
1018
 
1019
  for chunk in split_message_for_whatsapp(message):
1020
  try:
1021
  payload = {"phone_number": phone_number, "message_body": chunk}
1022
+ # Enhanced logging
1023
+ logger.info(f"[WhatsJet][DEBUG] URL: {url}")
1024
+ logger.info(f"[WhatsJet][DEBUG] Payload: {json.dumps(payload)}")
1025
+ response = httpx.post(
1026
+ url,
1027
+ json=payload,
1028
+ timeout=15
1029
+ )
1030
+ logger.info(f"[WhatsJet][DEBUG] Response status: {response.status_code}")
1031
+ logger.info(f"[WhatsJet][DEBUG] Response headers: {dict(response.headers)}")
1032
+ logger.info(f"[WhatsJet][DEBUG] Response body: {response.text}")
1033
+ response.raise_for_status()
1034
+ logger.info(f"[WhatsJet] Text chunk sent successfully to {phone_number}")
1035
  except Exception as e:
1036
+ import traceback
1037
+ logger.error(f"[WhatsJet][ERROR] Exception sending text chunk: {e}\nTraceback: {traceback.format_exc()}")
1038
  return False
 
1039
  logger.info(f"[WhatsJet] Successfully sent complete text message to {phone_number}")
1040
  return True
1041
 
 
3454
 
3455
  def get_product_image_path(product_name: str) -> str:
3456
  """
3457
+ Get the public URL for a product image if it exists in the uploads directory.
3458
+ Returns the public URL if found, otherwise falls back to static/images or None.
3459
  """
3460
  try:
3461
+ # Check uploads directory for exact match (case and spaces preserved)
3462
+ uploads_dir = "uploads"
3463
+ image_extensions = ['.jpg', '.jpeg', '.png']
3464
+ for ext in image_extensions:
3465
+ filename = f"{product_name}{ext}"
3466
+ local_path = os.path.join(uploads_dir, filename)
3467
+ if os.path.exists(local_path):
3468
+ # Construct the public URL for Hugging Face Space
3469
+ # (Assumes the Space is named dreamstream-1-chatbot)
3470
+ public_url = f"https://dreamstream-1-chatbot.hf.space/uploads/{filename.replace(' ', '%20')}"
3471
+ logger.info(f"[Image] Found product image in uploads: {public_url}")
3472
+ return public_url
3473
+ # Fallback to static/images (old logic)
3474
  images_dir = "static/images"
3475
  os.makedirs(images_dir, exist_ok=True)
 
 
3476
  safe_name = re.sub(r'[^\w\s-]', '', product_name).replace(' ', '_').lower()
 
 
3477
  image_extensions = ['.jpg', '.jpeg', '.png', '.webp', '.gif']
 
3478
  for ext in image_extensions:
3479
  image_path = os.path.join(images_dir, f"{safe_name}{ext}")
3480
  if os.path.exists(image_path):
3481
  logger.info(f"[Image] Found product image: {image_path}")
3482
  return image_path
 
 
3483
  default_image_path = os.path.join(images_dir, "default_product.jpg")
3484
  if os.path.exists(default_image_path):
3485
  logger.info(f"[Image] Using default product image: {default_image_path}")
3486
  return default_image_path
 
3487
  logger.warning(f"[Image] No image found for product: {product_name}")
3488
  return None
 
3489
  except Exception as e:
3490
  logger.error(f"[Image] Error getting product image path: {e}")
3491
  return None
 
3667
  product_name = product.get('Product Name', 'Unknown Product')
3668
  details = generate_veterinary_product_response(product, user_context)
3669
  image_url = product.get('Images', '').strip() if 'Images' in product else ''
3670
+
3671
+ # Force image URL for Respira Aid Plus (use cPanel public URL)
3672
+ if product_name.lower().strip() == "respira aid plus":
3673
+ image_url = "https://amgocus.com/uploads/images/Respira%20Aid%20Plus.jpg"
3674
+
3675
+ logger.info(f"[Product] Processing image for product: {product_name}")
3676
+ logger.info(f"[Product] Image URL from CSV: {image_url}")
3677
+
3678
  try:
3679
+ # First, check if we have an image URL from CSV
3680
  if image_url:
3681
  # Convert Google Drive link to direct download if needed
3682
  if 'drive.google.com' in image_url:
3683
+ logger.info(f"[Product] Converting Google Drive link: {image_url}")
3684
  if '/d/' in image_url:
3685
  file_id = image_url.split('/d/')[1].split('/')[0]
3686
  elif 'id=' in image_url:
 
3689
  file_id = ''
3690
  if file_id:
3691
  image_url = f"https://drive.google.com/uc?export=download&id={file_id}"
3692
+ logger.info(f"[Product] Converted to direct download URL: {image_url}")
3693
+
3694
+ # Use the public URL directly for WhatsApp API
3695
+ media_type = 'image/jpeg'
3696
+ filename = f"{product_name.replace(' ', '_')}.jpg"
3697
+
3698
+ # Test the image URL first
3699
+ try:
3700
+ logger.info(f"[Product] Testing image URL accessibility: {image_url}")
3701
+ test_response = requests.head(image_url, timeout=10)
3702
+ if test_response.status_code != 200:
3703
+ logger.warning(f"[Product] Image URL not accessible (status {test_response.status_code}): {image_url}")
3704
+ raise Exception(f"Image URL not accessible: {test_response.status_code}")
3705
+ logger.info(f"[Product] Image URL is accessible")
3706
+ except Exception as e:
3707
+ logger.warning(f"[Product] Failed to test image URL {image_url}: {e}")
3708
+ image_url = None
3709
+
3710
+ # Send using public URL (not local file)
3711
+ if image_url:
3712
+ logger.info(f"[Product] Attempting to send image from CSV URL for: {product_name}")
3713
  success = send_whatsjet_message(
3714
  from_number,
3715
  details,
3716
  media_type=media_type,
3717
+ media_path=image_url, # Use public URL directly
3718
  filename=filename
3719
  )
3720
+
3721
  if success:
3722
+ logger.info(f"[Product] Successfully sent image from CSV link with caption for product: {product_name}")
3723
  return
3724
  else:
3725
+ logger.warning(f"[Product] Failed to send image from CSV link, trying fallback: {product_name}")
3726
+
3727
+ # Fallback 1: Try with a known public test image
3728
+ logger.info(f"[Product] Trying public test image for: {product_name}")
3729
+ test_image_url = "https://www.w3schools.com/w3images/lights.jpg"
3730
+ media_type = 'image/jpeg'
3731
+ filename = f"{product_name.replace(' ', '_')}.jpg"
3732
+
3733
+ success = send_whatsjet_message(
3734
+ from_number,
3735
+ details,
3736
+ media_type=media_type,
3737
+ media_path=test_image_url,
3738
+ filename=filename
3739
+ )
3740
+
3741
+ if success:
3742
+ logger.info(f"[Product] Successfully sent test image with caption for product: {product_name}")
3743
+ return
3744
+
3745
+ # Fallback 2: Try local uploads directory (public URL)
3746
+ logger.info(f"[Product] Trying local uploads directory for: {product_name}")
3747
  image_path = get_product_image_path(product_name)
3748
+ if image_path and (image_path.startswith('http') or os.path.exists(image_path)):
3749
  media_type = get_product_image_media_type(image_path)
3750
  filename = f"{product_name.replace(' ', '_')}.jpg"
3751
+
3752
+ # If it's already a public URL, use it directly
3753
+ if image_path.startswith('http'):
3754
+ media_path = image_path
3755
+ logger.info(f"[Product] Using existing public URL: {media_path}")
3756
+ else:
3757
+ # Convert local path to public URL
3758
+ media_path = f"https://dreamstream-1-chatbot.hf.space/uploads/{os.path.basename(image_path).replace(' ', '%20')}"
3759
+ logger.info(f"[Product] Converted local path to public URL: {media_path}")
3760
+
3761
  success = send_whatsjet_message(
3762
  from_number,
3763
  details,
3764
  media_type=media_type,
3765
+ media_path=media_path, # Use public URL
3766
  filename=filename
3767
  )
3768
+
3769
  if success:
3770
+ logger.info(f"[Product] Successfully sent image with caption for product: {product_name}")
3771
  else:
3772
  logger.warning(f"[Product] Failed to send image, sending text only: {product_name}")
3773
  send_whatsjet_message(from_number, details)
3774
  else:
3775
+ # No image available, send text only
3776
+ logger.info(f"[Product] No image available, sending text only for: {product_name}")
3777
  send_whatsjet_message(from_number, details)
3778
+
3779
  except Exception as e:
3780
  logger.error(f"[Product] Error sending product image with caption: {e}")
3781
+ logger.info(f"[Product] Falling back to text-only message for: {product_name}")
3782
  send_whatsjet_message(from_number, details)
 
 
 
 
 
 
3783
 
3784
  @app.get("/test-product-image-with-caption")
3785
  async def test_product_image_with_caption(phone: str):
 
3799
  except Exception as e:
3800
  return {"error": str(e)}
3801
 
3802
+ @app.get("/test-image-sending")
3803
+ async def test_image_sending(phone: str, image_url: str = "https://www.w3schools.com/w3images/lights.jpg"):
3804
+ """
3805
+ Test endpoint to send a test image with caption to debug image sending functionality.
3806
+ """
3807
+ try:
3808
+ test_message = f"""🧪 *Test Image Message*
3809
+
3810
+ This is a test message to verify image sending functionality.
3811
+
3812
+ 📸 *Image Details:*
3813
+ • URL: {image_url}
3814
+ • Type: JPEG
3815
+ • Purpose: Testing WhatsJet API
3816
+
3817
+ 💬 *Test Options:*
3818
+ 1️⃣ Send another test image
3819
+ 2️⃣ Test with different URL
3820
+ 3️⃣ Back to main menu
3821
+
3822
+ Please confirm if you received both the image and this text message."""
3823
+
3824
+ success = send_whatsjet_message(
3825
+ phone,
3826
+ test_message,
3827
+ media_type="image/jpeg",
3828
+ media_path=image_url,
3829
+ filename="test_image.jpg"
3830
+ )
3831
+
3832
+ if success:
3833
+ return {
3834
+ "status": "success",
3835
+ "phone": phone,
3836
+ "image_url": image_url,
3837
+ "message": "Test image and caption sent successfully"
3838
+ }
3839
+ else:
3840
+ return {
3841
+ "status": "failed",
3842
+ "phone": phone,
3843
+ "image_url": image_url,
3844
+ "message": "Failed to send test image"
3845
+ }
3846
+ except Exception as e:
3847
+ return {"error": str(e), "phone": phone, "image_url": image_url}
3848
+
3849
+ @app.get("/debug-whatsjet")
3850
+ async def debug_whatsjet():
3851
+ """
3852
+ Debug endpoint to check WhatsJet configuration and test basic functionality.
3853
+ """
3854
+ try:
3855
+ # Check environment variables
3856
+ config_status = {
3857
+ "WHATSJET_API_URL": bool(WHATSJET_API_URL),
3858
+ "WHATSJET_VENDOR_UID": bool(WHATSJET_VENDOR_UID),
3859
+ "WHATSJET_API_TOKEN": bool(WHATSJET_API_TOKEN),
3860
+ "all_configured": all([WHATSJET_API_URL, WHATSJET_VENDOR_UID, WHATSJET_API_TOKEN])
3861
+ }
3862
+
3863
+ # Test basic text message if configured
3864
+ test_result = None
3865
+ if config_status["all_configured"]:
3866
+ try:
3867
+ # Test with a simple text message
3868
+ test_phone = "1234567890" # Dummy phone for testing
3869
+ test_success = send_whatsjet_message(
3870
+ test_phone,
3871
+ "🧪 WhatsJet API Test Message\n\nThis is a test to verify API connectivity.",
3872
+ )
3873
+ test_result = {
3874
+ "success": test_success,
3875
+ "message": "API test completed (dummy phone number used)"
3876
+ }
3877
+ except Exception as e:
3878
+ test_result = {
3879
+ "success": False,
3880
+ "error": str(e)
3881
+ }
3882
+
3883
+ return {
3884
+ "timestamp": datetime.now().isoformat(),
3885
+ "config_status": config_status,
3886
+ "test_result": test_result,
3887
+ "api_url": WHATSJET_API_URL if config_status["all_configured"] else "Not configured",
3888
+ "vendor_uid": WHATSJET_VENDOR_UID if config_status["all_configured"] else "Not configured"
3889
+ }
3890
+ except Exception as e:
3891
+ return {"error": str(e), "timestamp": datetime.now().isoformat()}
3892
+
3893
+ @app.get("/test-whatsjet-payloads")
3894
+ async def test_whatsjet_payloads(phone: str):
3895
+ """
3896
+ Test different WhatsJet API payload formats to identify the correct one for image sending.
3897
+ """
3898
+ if not all([WHATSJET_API_URL, WHATSJET_VENDOR_UID, WHATSJET_API_TOKEN]):
3899
+ return {"error": "WhatsJet not configured"}
3900
+
3901
+ url = f"{WHATSJET_API_URL}/{WHATSJET_VENDOR_UID}/contact/send-message?token={WHATSJET_API_TOKEN}"
3902
+ test_image_url = "https://www.w3schools.com/w3images/lights.jpg"
3903
+
3904
+ # Download test image
3905
+ try:
3906
+ response = requests.get(test_image_url, timeout=10)
3907
+ response.raise_for_status()
3908
+ image_content = response.content
3909
+ image_b64 = base64.b64encode(image_content).decode('utf-8')
3910
+ except Exception as e:
3911
+ return {"error": f"Failed to download test image: {e}"}
3912
+
3913
+ results = {}
3914
+
3915
+ # Test different payload formats
3916
+ test_payloads = [
3917
+ {
3918
+ "name": "Format 1: Standard with media_content",
3919
+ "payload": {
3920
+ "phone_number": phone,
3921
+ "media_type": "image/jpeg",
3922
+ "media_content": image_b64,
3923
+ "media_filename": "test.jpg",
3924
+ "message_body": ""
3925
+ }
3926
+ },
3927
+ {
3928
+ "name": "Format 2: With media_url instead of media_content",
3929
+ "payload": {
3930
+ "phone_number": phone,
3931
+ "media_type": "image/jpeg",
3932
+ "media_url": test_image_url,
3933
+ "media_filename": "test.jpg",
3934
+ "message_body": ""
3935
+ }
3936
+ },
3937
+ {
3938
+ "name": "Format 3: With file field",
3939
+ "payload": {
3940
+ "phone_number": phone,
3941
+ "file": image_b64,
3942
+ "file_type": "image/jpeg",
3943
+ "filename": "test.jpg",
3944
+ "message_body": ""
3945
+ }
3946
+ },
3947
+ {
3948
+ "name": "Format 4: With attachment field",
3949
+ "payload": {
3950
+ "phone_number": phone,
3951
+ "attachment": image_b64,
3952
+ "attachment_type": "image/jpeg",
3953
+ "attachment_name": "test.jpg",
3954
+ "message_body": ""
3955
+ }
3956
+ },
3957
+ {
3958
+ "name": "Format 5: With image field",
3959
+ "payload": {
3960
+ "phone_number": phone,
3961
+ "image": image_b64,
3962
+ "image_type": "image/jpeg",
3963
+ "image_name": "test.jpg",
3964
+ "message_body": ""
3965
+ }
3966
+ }
3967
+ ]
3968
+
3969
+ for test in test_payloads:
3970
+ try:
3971
+ logger.info(f"[WhatsJet] Testing payload format: {test['name']}")
3972
+ response = httpx.post(url, json=test['payload'], timeout=30)
3973
+
3974
+ results[test['name']] = {
3975
+ "status_code": response.status_code,
3976
+ "success": response.status_code == 200,
3977
+ "response_body": response.text[:200] if response.text else "No response body"
3978
+ }
3979
+
3980
+ logger.info(f"[WhatsJet] {test['name']} - Status: {response.status_code}")
3981
+
3982
+ except Exception as e:
3983
+ results[test['name']] = {
3984
+ "status_code": "Error",
3985
+ "success": False,
3986
+ "error": str(e)
3987
+ }
3988
+ logger.error(f"[WhatsJet] {test['name']} - Error: {e}")
3989
+
3990
+ return {
3991
+ "timestamp": datetime.now().isoformat(),
3992
+ "phone": phone,
3993
+ "test_image_url": test_image_url,
3994
+ "results": results
3995
+ }
3996
+
3997
  if __name__ == "__main__":
3998
  # Launch FastAPI app
3999
  import uvicorn
4000
+ uvicorn.run(app, host="0.0.0.0", port=7860)