DreamStream-1 commited on
Commit
648573d
·
verified ·
1 Parent(s): dd36977

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +293 -67
app.py CHANGED
@@ -900,53 +900,123 @@ def send_whatsjet_message(phone_number: str, message: str, media_type: str = Non
900
  try:
901
  # Download image if it's a URL
902
  if media_path.startswith("http://") or media_path.startswith("https://"):
903
- response = requests.get(media_path, stream=True, timeout=15)
904
- response.raise_for_status()
905
- media_content = response.content
906
- logger.info(f"[WhatsJet][DEBUG] Downloaded image content-type: {response.headers.get('Content-Type')}")
907
- logger.info(f"[WhatsJet][DEBUG] First 20 bytes: {media_content[:20]}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
908
  else:
909
- with open(media_path, 'rb') as f:
910
- media_content = f.read()
911
- media_b64 = base64.b64encode(media_content).decode('utf-8')
912
- payload = {
913
- "phone_number": phone_number,
914
- "media_type": media_type,
915
- "media_content": media_b64,
916
- "media_filename": filename or os.path.basename(media_path) if not media_path.startswith('http') else filename or 'image.jpg',
917
- "message_body": "" # No caption
918
- }
919
- # Send image (no caption)
920
  try:
 
 
 
 
 
 
 
 
 
921
  response = httpx.post(
922
  url,
923
- json=payload,
924
- timeout=15
 
925
  )
926
  response.raise_for_status()
927
- logger.info(f"[WhatsJet] Media image sent successfully to {phone_number}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
928
  except Exception as e:
929
- logger.error(f"[WhatsJet] Exception sending media image: {e}")
930
- return False
931
- # Send caption as a separate text message
932
- if message.strip():
933
- for chunk in split_message_for_whatsapp(message):
934
- try:
935
- text_payload = {"phone_number": phone_number, "message_body": chunk}
936
- text_response = httpx.post(
937
- url,
938
- json=text_payload,
939
- timeout=15
940
- )
941
- text_response.raise_for_status()
942
- logger.info(f"[WhatsJet] Text chunk (caption) sent successfully to {phone_number}")
943
- except Exception as e:
944
- logger.error(f"[WhatsJet] Exception sending text chunk (caption): {e}")
945
- return False
946
- return True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
947
  except Exception as e:
948
  logger.error(f"[WhatsJet] Exception preparing media image: {str(e)}")
949
- return False
 
950
 
951
  # Handle other media messages (existing logic)
952
  if media_type and media_path:
@@ -960,26 +1030,50 @@ def send_whatsjet_message(phone_number: str, message: str, media_type: str = Non
960
  else:
961
  with open(media_path, 'rb') as f:
962
  media_content = f.read()
963
- media_b64 = base64.b64encode(media_content).decode('utf-8')
964
- payload = {
965
- "phone_number": phone_number,
966
- "message_body": message,
967
- 'media_type': media_type,
968
- 'media_content': media_b64,
969
- 'media_filename': filename or os.path.basename(media_path) if not media_path.startswith('http') else filename or 'file.bin'
970
- }
971
  try:
 
 
 
 
 
 
 
 
972
  response = httpx.post(
973
  url,
974
- json=payload,
975
- timeout=15
 
976
  )
977
  response.raise_for_status()
978
- logger.info(f"[WhatsJet] Media message sent successfully to {phone_number}")
979
  return True
 
980
  except Exception as e:
981
- logger.error(f"[WhatsJet] Exception sending media message: {e}")
982
- return False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
983
  except Exception as e:
984
  logger.error(f"[WhatsJet] Exception preparing media message: {str(e)}")
985
  return False
@@ -3637,11 +3731,15 @@ async def send_product_image_with_caption(from_number: str, product: Dict[str, A
3637
  details = generate_veterinary_product_response(product, user_context)
3638
  image_url = product.get('Images', '').strip() if 'Images' in product else ''
3639
 
 
 
 
3640
  try:
3641
  # First, check if we have an image URL from CSV
3642
  if image_url:
3643
  # Convert Google Drive link to direct download if needed
3644
  if 'drive.google.com' in image_url:
 
3645
  if '/d/' in image_url:
3646
  file_id = image_url.split('/d/')[1].split('/')[0]
3647
  elif 'id=' in image_url:
@@ -3650,27 +3748,61 @@ async def send_product_image_with_caption(from_number: str, product: Dict[str, A
3650
  file_id = ''
3651
  if file_id:
3652
  image_url = f"https://drive.google.com/uc?export=download&id={file_id}"
 
3653
 
3654
  # Use the public URL directly for WhatsApp API
3655
  media_type = 'image/jpeg'
3656
  filename = f"{product_name.replace(' ', '_')}.jpg"
3657
 
3658
- # Send using public URL (not local file)
3659
- success = send_whatsjet_message(
3660
- from_number,
3661
- details,
3662
- media_type=media_type,
3663
- media_path=image_url, # Use public URL directly
3664
- filename=filename
3665
- )
 
 
 
3666
 
3667
- if success:
3668
- logger.info(f"[Product] Sent image from CSV link with caption for product: {product_name}")
3669
- return
3670
- else:
3671
- logger.warning(f"[Product] Failed to send image from CSV link, trying fallback: {product_name}")
 
 
 
 
 
 
 
 
 
 
 
3672
 
3673
- # Fallback to local uploads directory (public URL)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3674
  image_path = get_product_image_path(product_name)
3675
  if image_path and (image_path.startswith('http') or os.path.exists(image_path)):
3676
  media_type = get_product_image_media_type(image_path)
@@ -3679,9 +3811,11 @@ async def send_product_image_with_caption(from_number: str, product: Dict[str, A
3679
  # If it's already a public URL, use it directly
3680
  if image_path.startswith('http'):
3681
  media_path = image_path
 
3682
  else:
3683
  # Convert local path to public URL
3684
  media_path = f"https://dreamstream-1-chatbot.hf.space/uploads/{os.path.basename(image_path).replace(' ', '%20')}"
 
3685
 
3686
  success = send_whatsjet_message(
3687
  from_number,
@@ -3692,17 +3826,18 @@ async def send_product_image_with_caption(from_number: str, product: Dict[str, A
3692
  )
3693
 
3694
  if success:
3695
- logger.info(f"[Product] Sent image with caption for product: {product_name}")
3696
  else:
3697
  logger.warning(f"[Product] Failed to send image, sending text only: {product_name}")
3698
  send_whatsjet_message(from_number, details)
3699
  else:
3700
  # No image available, send text only
 
3701
  send_whatsjet_message(from_number, details)
3702
- logger.info(f"[Product] Sent product info without image: {product_name}")
3703
 
3704
  except Exception as e:
3705
  logger.error(f"[Product] Error sending product image with caption: {e}")
 
3706
  send_whatsjet_message(from_number, details)
3707
 
3708
  @app.get("/test-product-image-with-caption")
@@ -3723,6 +3858,97 @@ async def test_product_image_with_caption(phone: str):
3723
  except Exception as e:
3724
  return {"error": str(e)}
3725
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3726
  if __name__ == "__main__":
3727
  # Launch FastAPI app
3728
  import uvicorn
 
900
  try:
901
  # Download image if it's a URL
902
  if media_path.startswith("http://") or media_path.startswith("https://"):
903
+ try:
904
+ response = requests.get(media_path, stream=True, timeout=15)
905
+ response.raise_for_status()
906
+ media_content = response.content
907
+ content_type = response.headers.get('Content-Type', '')
908
+ logger.info(f"[WhatsJet][DEBUG] Downloaded image content-type: {content_type}")
909
+ logger.info(f"[WhatsJet][DEBUG] Image size: {len(media_content)} bytes")
910
+ logger.info(f"[WhatsJet][DEBUG] First 20 bytes: {media_content[:20]}")
911
+
912
+ # Validate image content
913
+ if len(media_content) == 0:
914
+ logger.error("[WhatsJet] Downloaded image is empty")
915
+ return False
916
+
917
+ # Check if content is actually an image
918
+ if not content_type.startswith('image/'):
919
+ logger.warning(f"[WhatsJet] URL does not return an image: {content_type}")
920
+
921
+ except Exception as e:
922
+ logger.error(f"[WhatsJet] Failed to download image from {media_path}: {e}")
923
+ return False
924
  else:
925
+ try:
926
+ with open(media_path, 'rb') as f:
927
+ media_content = f.read()
928
+ logger.info(f"[WhatsJet][DEBUG] Local image size: {len(media_content)} bytes")
929
+ except Exception as e:
930
+ logger.error(f"[WhatsJet] Failed to read local image {media_path}: {e}")
931
+ return False
932
+
933
+ # Try method 1: Send as multipart form data with file upload
 
 
934
  try:
935
+ files = {
936
+ 'media': (filename or 'image.jpg', media_content, media_type)
937
+ }
938
+ data = {
939
+ 'phone_number': phone_number,
940
+ 'message_body': '' # No caption with image
941
+ }
942
+
943
+ logger.info(f"[WhatsJet] Attempting multipart upload for {phone_number}")
944
  response = httpx.post(
945
  url,
946
+ data=data,
947
+ files=files,
948
+ timeout=30
949
  )
950
  response.raise_for_status()
951
+ logger.info(f"[WhatsJet] Media image sent successfully via multipart upload to {phone_number}")
952
+
953
+ # Send caption as separate text message
954
+ if message.strip():
955
+ for chunk in split_message_for_whatsapp(message):
956
+ try:
957
+ text_payload = {"phone_number": phone_number, "message_body": chunk}
958
+ text_response = httpx.post(
959
+ url,
960
+ json=text_payload,
961
+ timeout=15
962
+ )
963
+ text_response.raise_for_status()
964
+ logger.info(f"[WhatsJet] Text chunk (caption) sent successfully to {phone_number}")
965
+ except Exception as e:
966
+ logger.error(f"[WhatsJet] Exception sending text chunk (caption): {e}")
967
+ return False
968
+ return True
969
+
970
  except Exception as e:
971
+ logger.warning(f"[WhatsJet] Multipart upload failed, trying base64 method: {e}")
972
+
973
+ # Try method 2: Send as base64 encoded content
974
+ try:
975
+ media_b64 = base64.b64encode(media_content).decode('utf-8')
976
+ payload = {
977
+ "phone_number": phone_number,
978
+ "media_type": media_type,
979
+ "media_content": media_b64,
980
+ "media_filename": filename or os.path.basename(media_path) if not media_path.startswith('http') else filename or 'image.jpg',
981
+ "message_body": "" # No caption
982
+ }
983
+
984
+ logger.info(f"[WhatsJet] Attempting base64 upload for {phone_number}")
985
+ response = httpx.post(
986
+ url,
987
+ json=payload,
988
+ timeout=30
989
+ )
990
+ response.raise_for_status()
991
+ logger.info(f"[WhatsJet] Media image sent successfully via base64 to {phone_number}")
992
+
993
+ # Send caption as separate text message
994
+ if message.strip():
995
+ for chunk in split_message_for_whatsapp(message):
996
+ try:
997
+ text_payload = {"phone_number": phone_number, "message_body": chunk}
998
+ text_response = httpx.post(
999
+ url,
1000
+ json=text_payload,
1001
+ timeout=15
1002
+ )
1003
+ text_response.raise_for_status()
1004
+ logger.info(f"[WhatsJet] Text chunk (caption) sent successfully to {phone_number}")
1005
+ except Exception as e:
1006
+ logger.error(f"[WhatsJet] Exception sending text chunk (caption): {e}")
1007
+ return False
1008
+ return True
1009
+
1010
+ except Exception as e:
1011
+ logger.error(f"[WhatsJet] Base64 method also failed: {e}")
1012
+ # Fallback: send text only
1013
+ logger.warning(f"[WhatsJet] Both image methods failed, sending text only for {phone_number}")
1014
+ return send_whatsjet_message(phone_number, message)
1015
+
1016
  except Exception as e:
1017
  logger.error(f"[WhatsJet] Exception preparing media image: {str(e)}")
1018
+ # Fallback: send text only
1019
+ return send_whatsjet_message(phone_number, message)
1020
 
1021
  # Handle other media messages (existing logic)
1022
  if media_type and media_path:
 
1030
  else:
1031
  with open(media_path, 'rb') as f:
1032
  media_content = f.read()
1033
+
1034
+ # Try multipart first, then base64
 
 
 
 
 
 
1035
  try:
1036
+ files = {
1037
+ 'media': (filename or 'file.bin', media_content, media_type)
1038
+ }
1039
+ data = {
1040
+ 'phone_number': phone_number,
1041
+ 'message_body': message
1042
+ }
1043
+
1044
  response = httpx.post(
1045
  url,
1046
+ data=data,
1047
+ files=files,
1048
+ timeout=30
1049
  )
1050
  response.raise_for_status()
1051
+ logger.info(f"[WhatsJet] Media message sent successfully via multipart to {phone_number}")
1052
  return True
1053
+
1054
  except Exception as e:
1055
+ logger.warning(f"[WhatsJet] Multipart upload failed for media, trying base64: {e}")
1056
+
1057
+ media_b64 = base64.b64encode(media_content).decode('utf-8')
1058
+ payload = {
1059
+ "phone_number": phone_number,
1060
+ "message_body": message,
1061
+ 'media_type': media_type,
1062
+ 'media_content': media_b64,
1063
+ 'media_filename': filename or os.path.basename(media_path) if not media_path.startswith('http') else filename or 'file.bin'
1064
+ }
1065
+ try:
1066
+ response = httpx.post(
1067
+ url,
1068
+ json=payload,
1069
+ timeout=30
1070
+ )
1071
+ response.raise_for_status()
1072
+ logger.info(f"[WhatsJet] Media message sent successfully via base64 to {phone_number}")
1073
+ return True
1074
+ except Exception as e:
1075
+ logger.error(f"[WhatsJet] Exception sending media message: {e}")
1076
+ return False
1077
  except Exception as e:
1078
  logger.error(f"[WhatsJet] Exception preparing media message: {str(e)}")
1079
  return False
 
3731
  details = generate_veterinary_product_response(product, user_context)
3732
  image_url = product.get('Images', '').strip() if 'Images' in product else ''
3733
 
3734
+ logger.info(f"[Product] Processing image for product: {product_name}")
3735
+ logger.info(f"[Product] Image URL from CSV: {image_url}")
3736
+
3737
  try:
3738
  # First, check if we have an image URL from CSV
3739
  if image_url:
3740
  # Convert Google Drive link to direct download if needed
3741
  if 'drive.google.com' in image_url:
3742
+ logger.info(f"[Product] Converting Google Drive link: {image_url}")
3743
  if '/d/' in image_url:
3744
  file_id = image_url.split('/d/')[1].split('/')[0]
3745
  elif 'id=' in image_url:
 
3748
  file_id = ''
3749
  if file_id:
3750
  image_url = f"https://drive.google.com/uc?export=download&id={file_id}"
3751
+ logger.info(f"[Product] Converted to direct download URL: {image_url}")
3752
 
3753
  # Use the public URL directly for WhatsApp API
3754
  media_type = 'image/jpeg'
3755
  filename = f"{product_name.replace(' ', '_')}.jpg"
3756
 
3757
+ # Test the image URL first
3758
+ try:
3759
+ logger.info(f"[Product] Testing image URL accessibility: {image_url}")
3760
+ test_response = requests.head(image_url, timeout=10)
3761
+ if test_response.status_code != 200:
3762
+ logger.warning(f"[Product] Image URL not accessible (status {test_response.status_code}): {image_url}")
3763
+ raise Exception(f"Image URL not accessible: {test_response.status_code}")
3764
+ logger.info(f"[Product] Image URL is accessible")
3765
+ except Exception as e:
3766
+ logger.warning(f"[Product] Failed to test image URL {image_url}: {e}")
3767
+ image_url = None
3768
 
3769
+ # Send using public URL (not local file)
3770
+ if image_url:
3771
+ logger.info(f"[Product] Attempting to send image from CSV URL for: {product_name}")
3772
+ success = send_whatsjet_message(
3773
+ from_number,
3774
+ details,
3775
+ media_type=media_type,
3776
+ media_path=image_url, # Use public URL directly
3777
+ filename=filename
3778
+ )
3779
+
3780
+ if success:
3781
+ logger.info(f"[Product] Successfully sent image from CSV link with caption for product: {product_name}")
3782
+ return
3783
+ else:
3784
+ logger.warning(f"[Product] Failed to send image from CSV link, trying fallback: {product_name}")
3785
 
3786
+ # Fallback 1: Try with a known public test image
3787
+ logger.info(f"[Product] Trying public test image for: {product_name}")
3788
+ test_image_url = "https://www.w3schools.com/w3images/lights.jpg"
3789
+ media_type = 'image/jpeg'
3790
+ filename = f"{product_name.replace(' ', '_')}.jpg"
3791
+
3792
+ success = send_whatsjet_message(
3793
+ from_number,
3794
+ details,
3795
+ media_type=media_type,
3796
+ media_path=test_image_url,
3797
+ filename=filename
3798
+ )
3799
+
3800
+ if success:
3801
+ logger.info(f"[Product] Successfully sent test image with caption for product: {product_name}")
3802
+ return
3803
+
3804
+ # Fallback 2: Try local uploads directory (public URL)
3805
+ logger.info(f"[Product] Trying local uploads directory for: {product_name}")
3806
  image_path = get_product_image_path(product_name)
3807
  if image_path and (image_path.startswith('http') or os.path.exists(image_path)):
3808
  media_type = get_product_image_media_type(image_path)
 
3811
  # If it's already a public URL, use it directly
3812
  if image_path.startswith('http'):
3813
  media_path = image_path
3814
+ logger.info(f"[Product] Using existing public URL: {media_path}")
3815
  else:
3816
  # Convert local path to public URL
3817
  media_path = f"https://dreamstream-1-chatbot.hf.space/uploads/{os.path.basename(image_path).replace(' ', '%20')}"
3818
+ logger.info(f"[Product] Converted local path to public URL: {media_path}")
3819
 
3820
  success = send_whatsjet_message(
3821
  from_number,
 
3826
  )
3827
 
3828
  if success:
3829
+ logger.info(f"[Product] Successfully sent image with caption for product: {product_name}")
3830
  else:
3831
  logger.warning(f"[Product] Failed to send image, sending text only: {product_name}")
3832
  send_whatsjet_message(from_number, details)
3833
  else:
3834
  # No image available, send text only
3835
+ logger.info(f"[Product] No image available, sending text only for: {product_name}")
3836
  send_whatsjet_message(from_number, details)
 
3837
 
3838
  except Exception as e:
3839
  logger.error(f"[Product] Error sending product image with caption: {e}")
3840
+ logger.info(f"[Product] Falling back to text-only message for: {product_name}")
3841
  send_whatsjet_message(from_number, details)
3842
 
3843
  @app.get("/test-product-image-with-caption")
 
3858
  except Exception as e:
3859
  return {"error": str(e)}
3860
 
3861
+ @app.get("/test-image-sending")
3862
+ async def test_image_sending(phone: str, image_url: str = "https://www.w3schools.com/w3images/lights.jpg"):
3863
+ """
3864
+ Test endpoint to send a test image with caption to debug image sending functionality.
3865
+ """
3866
+ try:
3867
+ test_message = f"""🧪 *Test Image Message*
3868
+
3869
+ This is a test message to verify image sending functionality.
3870
+
3871
+ 📸 *Image Details:*
3872
+ • URL: {image_url}
3873
+ • Type: JPEG
3874
+ • Purpose: Testing WhatsJet API
3875
+
3876
+ 💬 *Test Options:*
3877
+ 1️⃣ Send another test image
3878
+ 2️⃣ Test with different URL
3879
+ 3️⃣ Back to main menu
3880
+
3881
+ Please confirm if you received both the image and this text message."""
3882
+
3883
+ success = send_whatsjet_message(
3884
+ phone,
3885
+ test_message,
3886
+ media_type="image/jpeg",
3887
+ media_path=image_url,
3888
+ filename="test_image.jpg"
3889
+ )
3890
+
3891
+ if success:
3892
+ return {
3893
+ "status": "success",
3894
+ "phone": phone,
3895
+ "image_url": image_url,
3896
+ "message": "Test image and caption sent successfully"
3897
+ }
3898
+ else:
3899
+ return {
3900
+ "status": "failed",
3901
+ "phone": phone,
3902
+ "image_url": image_url,
3903
+ "message": "Failed to send test image"
3904
+ }
3905
+ except Exception as e:
3906
+ return {"error": str(e), "phone": phone, "image_url": image_url}
3907
+
3908
+ @app.get("/debug-whatsjet")
3909
+ async def debug_whatsjet():
3910
+ """
3911
+ Debug endpoint to check WhatsJet configuration and test basic functionality.
3912
+ """
3913
+ try:
3914
+ # Check environment variables
3915
+ config_status = {
3916
+ "WHATSJET_API_URL": bool(WHATSJET_API_URL),
3917
+ "WHATSJET_VENDOR_UID": bool(WHATSJET_VENDOR_UID),
3918
+ "WHATSJET_API_TOKEN": bool(WHATSJET_API_TOKEN),
3919
+ "all_configured": all([WHATSJET_API_URL, WHATSJET_VENDOR_UID, WHATSJET_API_TOKEN])
3920
+ }
3921
+
3922
+ # Test basic text message if configured
3923
+ test_result = None
3924
+ if config_status["all_configured"]:
3925
+ try:
3926
+ # Test with a simple text message
3927
+ test_phone = "1234567890" # Dummy phone for testing
3928
+ test_success = send_whatsjet_message(
3929
+ test_phone,
3930
+ "🧪 WhatsJet API Test Message\n\nThis is a test to verify API connectivity.",
3931
+ )
3932
+ test_result = {
3933
+ "success": test_success,
3934
+ "message": "API test completed (dummy phone number used)"
3935
+ }
3936
+ except Exception as e:
3937
+ test_result = {
3938
+ "success": False,
3939
+ "error": str(e)
3940
+ }
3941
+
3942
+ return {
3943
+ "timestamp": datetime.now().isoformat(),
3944
+ "config_status": config_status,
3945
+ "test_result": test_result,
3946
+ "api_url": WHATSJET_API_URL if config_status["all_configured"] else "Not configured",
3947
+ "vendor_uid": WHATSJET_VENDOR_UID if config_status["all_configured"] else "Not configured"
3948
+ }
3949
+ except Exception as e:
3950
+ return {"error": str(e), "timestamp": datetime.now().isoformat()}
3951
+
3952
  if __name__ == "__main__":
3953
  # Launch FastAPI app
3954
  import uvicorn