import httpx from typing import Dict, Any, Optional from app.providers.base import BaseProvider from app.core.config import settings class WhatsAppCloudProvider(BaseProvider): """WhatsApp Cloud API provider (Meta)""" def __init__(self): self.access_token = settings.WHATSAPP_CLOUD_ACCESS_TOKEN self.phone_number_id = settings.WHATSAPP_CLOUD_PHONE_NUMBER_ID self.base_url = "https://graph.facebook.com/v18.0" def send_message( self, to: str, template_name: Optional[str], template_body: str, variables: Dict[str, str], language: str = "en" ) -> str: """ Send message via WhatsApp Cloud API. If template_name is provided, sends a template message. Otherwise, sends a text message. """ url = f"{self.base_url}/{self.phone_number_id}/messages" headers = { "Authorization": f"Bearer {self.access_token}", "Content-Type": "application/json" } if template_name: # Send template message # Build template parameters components = [] if variables: parameters = [ {"type": "text", "text": value} for value in variables.values() ] components.append({ "type": "body", "parameters": parameters }) payload = { "messaging_product": "whatsapp", "to": to.replace("+", ""), "type": "template", "template": { "name": template_name, "language": { "code": language }, "components": components } } else: # Send text message (only for contacts with conversation window) # Substitute variables in body message_text = template_body for key, value in variables.items(): message_text = message_text.replace(f"{{{{{key}}}}}", value) payload = { "messaging_product": "whatsapp", "to": to.replace("+", ""), "type": "text", "text": { "body": message_text } } try: with httpx.Client(timeout=30.0) as client: response = client.post(url, headers=headers, json=payload) response.raise_for_status() data = response.json() message_id = data.get("messages", [{}])[0].get("id") if not message_id: raise Exception("No message ID in response") return message_id except httpx.HTTPStatusError as e: raise Exception(f"WhatsApp API error: {e.response.status_code} - {e.response.text}") except Exception as e: raise Exception(f"Send error: {str(e)}") def get_provider_name(self) -> str: return "whatsapp_cloud"