Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
|
2 |
"""
|
3 |
Apex Biotical Veterinary WhatsApp Bot - Premium Edition
|
4 |
The most effective and accurate veterinary chatbot in the market
|
@@ -888,14 +888,47 @@ 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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
899 |
if media_type and media_path:
|
900 |
try:
|
901 |
with open(media_path, 'rb') as f:
|
@@ -908,7 +941,6 @@ def send_whatsjet_message(phone_number: str, message: str, media_type: str = Non
|
|
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,
|
@@ -925,14 +957,13 @@ def send_whatsjet_message(phone_number: str, message: str, media_type: str = Non
|
|
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,
|
@@ -947,7 +978,6 @@ def send_whatsjet_message(phone_number: str, message: str, media_type: str = Non
|
|
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 +3396,38 @@ load_products_data()
|
|
3366 |
|
3367 |
def get_product_image_path(product_name: str) -> str:
|
3368 |
"""
|
3369 |
-
Get the
|
3370 |
-
Returns the
|
3371 |
"""
|
3372 |
try:
|
3373 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
"""
|
3 |
Apex Biotical Veterinary WhatsApp Bot - Premium Edition
|
4 |
The most effective and accurate veterinary chatbot in the market
|
|
|
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 (now supports WhatsApp image+caption format)"""
|
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 WhatsApp image+caption messages (public URL only)
|
899 |
+
if media_type in ["image/jpeg", "image/png"] and media_path:
|
900 |
+
# If media_path is a URL, send as WhatsApp API expects
|
901 |
+
if isinstance(media_path, str) and (media_path.startswith("http://") or media_path.startswith("https://")):
|
902 |
+
payload = {
|
903 |
+
"messaging_product": "whatsapp",
|
904 |
+
"recipient_type": "individual",
|
905 |
+
"to": phone_number,
|
906 |
+
"type": "image",
|
907 |
+
"image": {
|
908 |
+
"link": media_path,
|
909 |
+
"caption": message[:1024] # WhatsApp caption limit
|
910 |
+
}
|
911 |
+
}
|
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] WhatsApp image+caption message sent successfully to {phone_number}")
|
920 |
+
return True
|
921 |
+
except Exception as e:
|
922 |
+
logger.error(f"[WhatsJet] Exception sending WhatsApp image+caption: {e}")
|
923 |
+
return False
|
924 |
+
else:
|
925 |
+
# If media_path is a local file, fallback to text only and log warning
|
926 |
+
logger.warning(f"[WhatsJet] Local file for image+caption not supported for WhatsApp API. Sending text only to {phone_number}.")
|
927 |
+
if message.strip():
|
928 |
+
return send_whatsjet_message(phone_number, message)
|
929 |
+
return False
|
930 |
+
|
931 |
+
# Handle other media messages (existing logic)
|
932 |
if media_type and media_path:
|
933 |
try:
|
934 |
with open(media_path, 'rb') as f:
|
|
|
941 |
'media_content': media_b64,
|
942 |
'media_filename': filename or os.path.basename(media_path)
|
943 |
}
|
|
|
944 |
try:
|
945 |
response = httpx.post(
|
946 |
url,
|
|
|
957 |
logger.error(f"[WhatsJet] Exception preparing media message: {str(e)}")
|
958 |
return False
|
959 |
|
960 |
+
# Handle text messages (existing logic)
|
961 |
if not message.strip():
|
962 |
return True # Don't send empty messages
|
963 |
|
964 |
for chunk in split_message_for_whatsapp(message):
|
965 |
try:
|
966 |
payload = {"phone_number": phone_number, "message_body": chunk}
|
|
|
967 |
try:
|
968 |
response = httpx.post(
|
969 |
url,
|
|
|
978 |
except Exception as e:
|
979 |
logger.error(f"[WhatsJet] Exception preparing text chunk: {str(e)}")
|
980 |
return False
|
|
|
981 |
logger.info(f"[WhatsJet] Successfully sent complete text message to {phone_number}")
|
982 |
return True
|
983 |
|
|
|
3396 |
|
3397 |
def get_product_image_path(product_name: str) -> str:
|
3398 |
"""
|
3399 |
+
Get the public URL for a product image if it exists in the uploads directory.
|
3400 |
+
Returns the public URL if found, otherwise falls back to static/images or None.
|
3401 |
"""
|
3402 |
try:
|
3403 |
+
# Check uploads directory for exact match (case and spaces preserved)
|
3404 |
+
uploads_dir = "uploads"
|
3405 |
+
image_extensions = ['.jpg', '.jpeg', '.png']
|
3406 |
+
for ext in image_extensions:
|
3407 |
+
filename = f"{product_name}{ext}"
|
3408 |
+
local_path = os.path.join(uploads_dir, filename)
|
3409 |
+
if os.path.exists(local_path):
|
3410 |
+
# Construct the public URL for Hugging Face Space
|
3411 |
+
# (Assumes the Space is named dreamstream-1-chatbot)
|
3412 |
+
public_url = f"https://dreamstream-1-chatbot.hf.space/uploads/{filename.replace(' ', '%20')}"
|
3413 |
+
logger.info(f"[Image] Found product image in uploads: {public_url}")
|
3414 |
+
return public_url
|
3415 |
+
# Fallback to static/images (old logic)
|
3416 |
images_dir = "static/images"
|
3417 |
os.makedirs(images_dir, exist_ok=True)
|
|
|
|
|
3418 |
safe_name = re.sub(r'[^\w\s-]', '', product_name).replace(' ', '_').lower()
|
|
|
|
|
3419 |
image_extensions = ['.jpg', '.jpeg', '.png', '.webp', '.gif']
|
|
|
3420 |
for ext in image_extensions:
|
3421 |
image_path = os.path.join(images_dir, f"{safe_name}{ext}")
|
3422 |
if os.path.exists(image_path):
|
3423 |
logger.info(f"[Image] Found product image: {image_path}")
|
3424 |
return image_path
|
|
|
|
|
3425 |
default_image_path = os.path.join(images_dir, "default_product.jpg")
|
3426 |
if os.path.exists(default_image_path):
|
3427 |
logger.info(f"[Image] Using default product image: {default_image_path}")
|
3428 |
return default_image_path
|
|
|
3429 |
logger.warning(f"[Image] No image found for product: {product_name}")
|
3430 |
return None
|
|
|
3431 |
except Exception as e:
|
3432 |
logger.error(f"[Image] Error getting product image path: {e}")
|
3433 |
return None
|