Fix SSL/TLS handshake failure - simplify httpx client creation
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- Removed manual SSL context creation that was causing handshake failures - Simplified create_http_client() to let httpx handle SSL negotiation directly - Disabled HTTP/2 for better compatibility - Added proper connection limits and timeouts - Updated all three client instantiations to await the async function - Improved overall SSL/TLS stability for Meta API connections
This commit is contained in:
parent
18de92aa3a
commit
9b6f053d86
94
backend/test_whatsapp_send.py
Normal file
94
backend/test_whatsapp_send.py
Normal file
@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script to send WhatsApp message via template
|
||||
Usage: python test_whatsapp_send.py
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables
|
||||
load_dotenv()
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Import after logging is configured
|
||||
from database import SessionLocal
|
||||
from whatsapp import WhatsAppService
|
||||
|
||||
|
||||
async def test_send_whatsapp():
|
||||
"""Test sending a WhatsApp message using hina_invitation template"""
|
||||
|
||||
db = SessionLocal()
|
||||
|
||||
try:
|
||||
# Initialize WhatsApp service
|
||||
service = WhatsAppService(db=db)
|
||||
|
||||
# Phone number to test (Israeli format)
|
||||
phone = "0504370045"
|
||||
|
||||
# Template parameters for hina_invitation
|
||||
params = {
|
||||
"contact_name": "דוד",
|
||||
"event_date": "17/05",
|
||||
"event_date_day": "17",
|
||||
"venue": "אולם הגן",
|
||||
"location": "ירושלים",
|
||||
"reception_time": "18:30",
|
||||
"ceremony_time": "19:00",
|
||||
"dinner_time": "20:00",
|
||||
"bride_name": "ורד",
|
||||
"groom_name": "דביר",
|
||||
"event_id": "f3122a7d-1d7c-4cc1-955d-1c6b7358bd25"
|
||||
}
|
||||
|
||||
print("\n" + "="*80)
|
||||
print("TESTING WHATSAPP MESSAGE SEND")
|
||||
print("="*80)
|
||||
print(f"Phone: {phone}")
|
||||
print(f"Template: hina_invitation")
|
||||
print(f"Parameters: {params}")
|
||||
print("="*80 + "\n")
|
||||
|
||||
# Send the message
|
||||
logger.info(f"Attempting to send WhatsApp message to {phone}")
|
||||
result = await service.send_by_template_key(
|
||||
template_key="wedding_invitation_by_vered",
|
||||
to_phone=phone,
|
||||
params=params
|
||||
)
|
||||
|
||||
print("\n" + "="*80)
|
||||
print("SUCCESS!")
|
||||
print("="*80)
|
||||
print(f"Result: {result}")
|
||||
print("="*80 + "\n")
|
||||
|
||||
return result
|
||||
|
||||
except Exception as e:
|
||||
print("\n" + "="*80)
|
||||
print("ERROR!")
|
||||
print("="*80)
|
||||
print(f"Error: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print("="*80 + "\n")
|
||||
raise
|
||||
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(test_send_whatsapp())
|
||||
@ -3,7 +3,6 @@ WhatsApp Cloud API Service
|
||||
Handles sending WhatsApp messages via Meta's API
|
||||
"""
|
||||
import os
|
||||
import ssl
|
||||
import httpx
|
||||
import certifi
|
||||
import re
|
||||
@ -15,32 +14,19 @@ from datetime import datetime
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def create_http_client() -> httpx.AsyncClient:
|
||||
async def create_http_client() -> httpx.AsyncClient:
|
||||
"""
|
||||
Create an httpx client with robust SSL configuration.
|
||||
Tries to use system certificates first, falls back to certifi.
|
||||
Create an httpx client with proper certificate verification.
|
||||
Uses certifi for CA bundle and lets httpx handle SSL/TLS negotiation.
|
||||
"""
|
||||
try:
|
||||
# Try using system SSL context with certifi certificates
|
||||
ssl_context = ssl.create_default_context(cafile=certifi.where())
|
||||
# Enable hostname checking but allow some TLS flexibility
|
||||
ssl_context.check_hostname = True
|
||||
ssl_context.verify_mode = ssl.CERT_REQUIRED
|
||||
# Use more compatible TLS versions
|
||||
ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
|
||||
|
||||
return httpx.AsyncClient(verify=ssl_context, timeout=30.0)
|
||||
except Exception as e:
|
||||
logger.warning(f"[WhatsApp] Failed to create SSL context with certifi: {e}. Trying default SSL.")
|
||||
try:
|
||||
# Fallback to default SSL context
|
||||
ssl_context = ssl.create_default_context()
|
||||
ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2
|
||||
return httpx.AsyncClient(verify=ssl_context, timeout=30.0)
|
||||
except Exception as e2:
|
||||
logger.error(f"[WhatsApp] Failed to create SSL context: {e2}. Using basic client.")
|
||||
# Last resort fallback - httpx will use its own SSL handling
|
||||
return httpx.AsyncClient(timeout=30.0)
|
||||
# Let httpx use certifi's CA bundle - simpler and more reliable
|
||||
return httpx.AsyncClient(
|
||||
verify=certifi.where(),
|
||||
timeout=httpx.Timeout(30.0, connect=10.0),
|
||||
http2=False, # Disable HTTP/2 to avoid compatibility issues
|
||||
limits=httpx.Limits(max_keepalive_connections=5, max_connections=10)
|
||||
)
|
||||
|
||||
|
||||
|
||||
class WhatsAppError(Exception):
|
||||
@ -195,7 +181,7 @@ class WhatsAppService:
|
||||
url = f"{self.base_url}/{self.phone_number_id}/messages"
|
||||
|
||||
try:
|
||||
async with create_http_client() as client:
|
||||
async with await create_http_client() as client:
|
||||
response = await client.post(
|
||||
url,
|
||||
json=payload,
|
||||
@ -325,7 +311,7 @@ class WhatsAppService:
|
||||
print("=" * 80 + "\n")
|
||||
|
||||
try:
|
||||
async with create_http_client() as client:
|
||||
async with await create_http_client() as client:
|
||||
response = await client.post(
|
||||
url,
|
||||
json=payload,
|
||||
@ -588,7 +574,7 @@ class WhatsAppService:
|
||||
|
||||
url = f"{self.base_url}/{self.phone_number_id}/messages"
|
||||
try:
|
||||
async with create_http_client() as client:
|
||||
async with await create_http_client() as client:
|
||||
response = await client.post(
|
||||
url,
|
||||
json=payload,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user