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
|