fix: add missing send_by_template_key method to WhatsAppService

This commit is contained in:
dvirlabs 2026-03-01 03:17:24 +02:00
parent d338722880
commit c50544d4bd

View File

@ -411,6 +411,123 @@ class WhatsAppService:
parameters=parameters
)
async def send_by_template_key(
self,
template_key: str,
to_phone: str,
params: dict,
) -> dict:
"""
Send a WhatsApp template message using the template registry.
Looks up *template_key* in whatsapp_templates.py, resolves header and
body parameter lists (with fallbacks) from *params*, then builds and
sends the Meta API payload dynamically.
Args:
template_key: Registry key (e.g. "wedding_invitation").
to_phone: Recipient phone number (normalized to E.164).
params: Dict of {param_key: value} for all placeholders.
Returns:
dict with message_id and status.
"""
from whatsapp_templates import get_template, build_params_list
tpl = get_template(template_key)
meta_name = tpl["meta_name"]
language_code = tpl.get("language_code", "he")
header_values, body_values = build_params_list(template_key, params)
to_e164 = self.normalize_phone_to_e164(to_phone)
if not self.validate_phone(to_e164):
raise WhatsAppError(f"Invalid phone number: {to_phone}")
components = []
if header_values:
components.append({
"type": "header",
"parameters": [{"type": "text", "text": str(v)} for v in header_values],
})
if body_values:
components.append({
"type": "body",
"parameters": [{"type": "text", "text": str(v)} for v in body_values],
})
# Handle url_button component if defined in template
url_btn = tpl.get("url_button", {})
if url_btn and url_btn.get("enabled"):
param_key = url_btn.get("param_key", "event_id")
btn_value = str(params.get(param_key, "")).strip()
if btn_value:
components.append({
"type": "button",
"sub_type": "url",
"index": str(url_btn.get("button_index", 0)),
"parameters": [{"type": "text", "text": btn_value}],
})
payload = {
"messaging_product": "whatsapp",
"to": to_e164,
"type": "template",
"template": {
"name": meta_name,
"language": {"code": language_code},
"components": components,
},
}
import json
logger.info(
f"[WhatsApp] send_by_template_key '{template_key}' → meta='{meta_name}' "
f"lang={language_code} to={to_e164} "
f"header_params={header_values} body_params={body_values}"
)
logger.debug(
"[WhatsApp] payload: %s",
json.dumps(payload, ensure_ascii=False),
)
url = f"{self.base_url}/{self.phone_number_id}/messages"
try:
async with httpx.AsyncClient() as client:
response = await client.post(
url,
json=payload,
headers=self.headers,
timeout=30.0,
)
if response.status_code not in (200, 201):
error_data = response.json()
error_msg = error_data.get("error", {}).get("message", "Unknown error")
logger.error(f"[WhatsApp] API error ({response.status_code}): {error_msg}")
raise WhatsAppError(
f"WhatsApp API error ({response.status_code}): {error_msg}"
)
result = response.json()
message_id = result.get("messages", [{}])[0].get("id")
logger.info(f"[WhatsApp] Message sent successfully via template key. ID: {message_id}")
return {
"message_id": message_id,
"status": "sent",
"to": to_e164,
"timestamp": datetime.utcnow().isoformat(),
"type": "template",
"template": meta_name,
}
except httpx.HTTPError as e:
raise WhatsAppError(f"HTTP request failed: {str(e)}")
except WhatsAppError:
raise
except Exception as e:
raise WhatsAppError(f"Failed to send WhatsApp template: {str(e)}")
def handle_webhook_verification(self, challenge: str) -> str:
"""
Handle webhook verification challenge from Meta