371 lines
13 KiB
Python
371 lines
13 KiB
Python
"""
|
|
WhatsApp Business Cloud API Schemas
|
|
"""
|
|
|
|
from pydantic import BaseModel, Field, validator
|
|
from typing import Optional, List, Dict, Any
|
|
from datetime import datetime
|
|
from enum import Enum
|
|
|
|
|
|
# ============================================================
|
|
# Enums
|
|
# ============================================================
|
|
|
|
class WhatsAppMessageType(str, Enum):
|
|
"""WhatsApp message types"""
|
|
TEMPLATE = "template"
|
|
TEXT = "text"
|
|
IMAGE = "image"
|
|
DOCUMENT = "document"
|
|
INTERACTIVE = "interactive"
|
|
|
|
|
|
class WhatsAppMessageStatus(str, Enum):
|
|
"""WhatsApp message delivery status"""
|
|
PENDING = "pending"
|
|
SENT = "sent"
|
|
DELIVERED = "delivered"
|
|
READ = "read"
|
|
FAILED = "failed"
|
|
|
|
|
|
class TemplateCategory(str, Enum):
|
|
"""WhatsApp template categories"""
|
|
MARKETING = "MARKETING"
|
|
UTILITY = "UTILITY"
|
|
AUTHENTICATION = "AUTHENTICATION"
|
|
|
|
|
|
# ============================================================
|
|
# Template Message Schemas
|
|
# ============================================================
|
|
|
|
class TemplateParameter(BaseModel):
|
|
"""Template parameter for dynamic content"""
|
|
type: str = Field(default="text", description="Parameter type (text, currency, date_time)")
|
|
text: Optional[str] = Field(None, description="Text value for the parameter")
|
|
|
|
class Config:
|
|
json_schema_extra = {
|
|
"example": {
|
|
"type": "text",
|
|
"text": "PO-2024-001"
|
|
}
|
|
}
|
|
|
|
|
|
class TemplateComponent(BaseModel):
|
|
"""Template component (header, body, buttons)"""
|
|
type: str = Field(..., description="Component type (header, body, button)")
|
|
parameters: Optional[List[TemplateParameter]] = Field(None, description="Component parameters")
|
|
sub_type: Optional[str] = Field(None, description="Button sub_type (quick_reply, url)")
|
|
index: Optional[int] = Field(None, description="Button index")
|
|
|
|
class Config:
|
|
json_schema_extra = {
|
|
"example": {
|
|
"type": "body",
|
|
"parameters": [
|
|
{"type": "text", "text": "PO-2024-001"},
|
|
{"type": "text", "text": "100.50"}
|
|
]
|
|
}
|
|
}
|
|
|
|
|
|
class TemplateMessageRequest(BaseModel):
|
|
"""Request to send a template message"""
|
|
template_name: str = Field(..., description="WhatsApp template name")
|
|
language: str = Field(default="es", description="Template language code")
|
|
components: List[TemplateComponent] = Field(..., description="Template components with parameters")
|
|
|
|
class Config:
|
|
json_schema_extra = {
|
|
"example": {
|
|
"template_name": "po_notification",
|
|
"language": "es",
|
|
"components": [
|
|
{
|
|
"type": "body",
|
|
"parameters": [
|
|
{"type": "text", "text": "PO-2024-001"},
|
|
{"type": "text", "text": "Supplier XYZ"},
|
|
{"type": "text", "text": "€1,250.00"}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
|
|
# ============================================================
|
|
# Send Message Schemas
|
|
# ============================================================
|
|
|
|
class SendWhatsAppMessageRequest(BaseModel):
|
|
"""Request to send a WhatsApp message"""
|
|
tenant_id: str = Field(..., description="Tenant ID")
|
|
recipient_phone: str = Field(..., description="Recipient phone number (E.164 format)")
|
|
recipient_name: Optional[str] = Field(None, description="Recipient name")
|
|
message_type: WhatsAppMessageType = Field(..., description="Message type")
|
|
template: Optional[TemplateMessageRequest] = Field(None, description="Template details (required for template messages)")
|
|
text: Optional[str] = Field(None, description="Text message body (for text messages)")
|
|
media_url: Optional[str] = Field(None, description="Media URL (for image/document messages)")
|
|
metadata: Optional[Dict[str, Any]] = Field(None, description="Additional metadata (PO number, order ID, etc.)")
|
|
notification_id: Optional[str] = Field(None, description="Link to existing notification")
|
|
|
|
@validator('recipient_phone')
|
|
def validate_phone(cls, v):
|
|
"""Validate E.164 phone format"""
|
|
if not v.startswith('+'):
|
|
raise ValueError('Phone number must be in E.164 format (starting with +)')
|
|
if len(v) < 10 or len(v) > 16:
|
|
raise ValueError('Phone number length must be between 10 and 16 characters')
|
|
return v
|
|
|
|
@validator('template')
|
|
def validate_template(cls, v, values):
|
|
"""Validate template is provided for template messages"""
|
|
if values.get('message_type') == WhatsAppMessageType.TEMPLATE and not v:
|
|
raise ValueError('Template details required for template messages')
|
|
return v
|
|
|
|
class Config:
|
|
json_schema_extra = {
|
|
"example": {
|
|
"tenant_id": "123e4567-e89b-12d3-a456-426614174000",
|
|
"recipient_phone": "+34612345678",
|
|
"recipient_name": "Supplier ABC",
|
|
"message_type": "template",
|
|
"template": {
|
|
"template_name": "po_notification",
|
|
"language": "es",
|
|
"components": [
|
|
{
|
|
"type": "body",
|
|
"parameters": [
|
|
{"type": "text", "text": "PO-2024-001"},
|
|
{"type": "text", "text": "€1,250.00"}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
"metadata": {
|
|
"po_number": "PO-2024-001",
|
|
"po_id": "123e4567-e89b-12d3-a456-426614174111"
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
class SendWhatsAppMessageResponse(BaseModel):
|
|
"""Response after sending a WhatsApp message"""
|
|
success: bool = Field(..., description="Whether message was sent successfully")
|
|
message_id: str = Field(..., description="Internal message ID")
|
|
whatsapp_message_id: Optional[str] = Field(None, description="WhatsApp's message ID")
|
|
status: WhatsAppMessageStatus = Field(..., description="Message status")
|
|
error_message: Optional[str] = Field(None, description="Error message if failed")
|
|
|
|
class Config:
|
|
json_schema_extra = {
|
|
"example": {
|
|
"success": True,
|
|
"message_id": "123e4567-e89b-12d3-a456-426614174222",
|
|
"whatsapp_message_id": "wamid.HBgNMzQ2MTIzNDU2Nzg5FQIAERgSMzY5RTFFNTdEQzZBRkVCODdBAA==",
|
|
"status": "sent",
|
|
"error_message": None
|
|
}
|
|
}
|
|
|
|
|
|
# ============================================================
|
|
# Webhook Schemas
|
|
# ============================================================
|
|
|
|
class WebhookValue(BaseModel):
|
|
"""Webhook notification value"""
|
|
messaging_product: str
|
|
metadata: Dict[str, Any]
|
|
contacts: Optional[List[Dict[str, Any]]] = None
|
|
messages: Optional[List[Dict[str, Any]]] = None
|
|
statuses: Optional[List[Dict[str, Any]]] = None
|
|
|
|
|
|
class WebhookEntry(BaseModel):
|
|
"""Webhook entry"""
|
|
id: str
|
|
changes: List[Dict[str, Any]]
|
|
|
|
|
|
class WhatsAppWebhook(BaseModel):
|
|
"""WhatsApp webhook payload"""
|
|
object: str
|
|
entry: List[WebhookEntry]
|
|
|
|
|
|
class WebhookVerification(BaseModel):
|
|
"""Webhook verification request"""
|
|
mode: str = Field(..., alias="hub.mode")
|
|
token: str = Field(..., alias="hub.verify_token")
|
|
challenge: str = Field(..., alias="hub.challenge")
|
|
|
|
class Config:
|
|
populate_by_name = True
|
|
|
|
|
|
# ============================================================
|
|
# Message Status Schemas
|
|
# ============================================================
|
|
|
|
class MessageStatusUpdate(BaseModel):
|
|
"""Message status update"""
|
|
whatsapp_message_id: str = Field(..., description="WhatsApp message ID")
|
|
status: WhatsAppMessageStatus = Field(..., description="New status")
|
|
timestamp: datetime = Field(..., description="Status update timestamp")
|
|
error_code: Optional[str] = Field(None, description="Error code if failed")
|
|
error_message: Optional[str] = Field(None, description="Error message if failed")
|
|
|
|
|
|
# ============================================================
|
|
# Template Management Schemas
|
|
# ============================================================
|
|
|
|
class WhatsAppTemplateCreate(BaseModel):
|
|
"""Create a WhatsApp template"""
|
|
tenant_id: Optional[str] = Field(None, description="Tenant ID (null for system templates)")
|
|
template_name: str = Field(..., description="Template name in WhatsApp")
|
|
template_key: str = Field(..., description="Internal template key")
|
|
display_name: str = Field(..., description="Display name")
|
|
description: Optional[str] = Field(None, description="Template description")
|
|
category: TemplateCategory = Field(..., description="Template category")
|
|
language: str = Field(default="es", description="Template language")
|
|
header_type: Optional[str] = Field(None, description="Header type (TEXT, IMAGE, DOCUMENT, VIDEO)")
|
|
header_text: Optional[str] = Field(None, max_length=60, description="Header text (max 60 chars)")
|
|
body_text: str = Field(..., description="Body text with {{1}}, {{2}} placeholders")
|
|
footer_text: Optional[str] = Field(None, max_length=60, description="Footer text (max 60 chars)")
|
|
parameters: Optional[List[Dict[str, Any]]] = Field(None, description="Parameter definitions")
|
|
buttons: Optional[List[Dict[str, Any]]] = Field(None, description="Button definitions")
|
|
|
|
class Config:
|
|
json_schema_extra = {
|
|
"example": {
|
|
"template_name": "po_notification",
|
|
"template_key": "po_notification_v1",
|
|
"display_name": "Purchase Order Notification",
|
|
"description": "Notify supplier of new purchase order",
|
|
"category": "UTILITY",
|
|
"language": "es",
|
|
"body_text": "Hola {{1}}, has recibido una nueva orden de compra {{2}} por un total de {{3}}.",
|
|
"parameters": [
|
|
{"name": "supplier_name", "example": "Proveedor ABC"},
|
|
{"name": "po_number", "example": "PO-2024-001"},
|
|
{"name": "total_amount", "example": "€1,250.00"}
|
|
]
|
|
}
|
|
}
|
|
|
|
|
|
class WhatsAppTemplateResponse(BaseModel):
|
|
"""WhatsApp template response"""
|
|
id: str
|
|
tenant_id: Optional[str]
|
|
template_name: str
|
|
template_key: str
|
|
display_name: str
|
|
description: Optional[str]
|
|
category: str
|
|
language: str
|
|
status: str
|
|
body_text: str
|
|
parameter_count: int
|
|
is_active: bool
|
|
sent_count: int
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
json_schema_extra = {
|
|
"example": {
|
|
"id": "123e4567-e89b-12d3-a456-426614174333",
|
|
"tenant_id": None,
|
|
"template_name": "po_notification",
|
|
"template_key": "po_notification_v1",
|
|
"display_name": "Purchase Order Notification",
|
|
"description": "Notify supplier of new purchase order",
|
|
"category": "UTILITY",
|
|
"language": "es",
|
|
"status": "APPROVED",
|
|
"body_text": "Hola {{1}}, has recibido una nueva orden de compra {{2}} por un total de {{3}}.",
|
|
"parameter_count": 3,
|
|
"is_active": True,
|
|
"sent_count": 125,
|
|
"created_at": "2024-01-15T10:30:00",
|
|
"updated_at": "2024-01-15T10:30:00"
|
|
}
|
|
}
|
|
|
|
|
|
# ============================================================
|
|
# Message Query Schemas
|
|
# ============================================================
|
|
|
|
class WhatsAppMessageResponse(BaseModel):
|
|
"""WhatsApp message response"""
|
|
id: str
|
|
tenant_id: str
|
|
notification_id: Optional[str]
|
|
whatsapp_message_id: Optional[str]
|
|
recipient_phone: str
|
|
recipient_name: Optional[str]
|
|
message_type: str
|
|
status: str
|
|
template_name: Optional[str]
|
|
template_language: Optional[str]
|
|
message_body: Optional[str]
|
|
sent_at: Optional[datetime]
|
|
delivered_at: Optional[datetime]
|
|
read_at: Optional[datetime]
|
|
failed_at: Optional[datetime]
|
|
error_message: Optional[str]
|
|
metadata: Optional[Dict[str, Any]]
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
|
|
|
|
class WhatsAppDeliveryStats(BaseModel):
|
|
"""WhatsApp delivery statistics"""
|
|
total_messages: int
|
|
sent: int
|
|
delivered: int
|
|
read: int
|
|
failed: int
|
|
pending: int
|
|
unique_recipients: int
|
|
total_conversations: int
|
|
delivery_rate: float
|
|
period: Dict[str, str]
|
|
|
|
class Config:
|
|
json_schema_extra = {
|
|
"example": {
|
|
"total_messages": 1500,
|
|
"sent": 1480,
|
|
"delivered": 1450,
|
|
"read": 1200,
|
|
"failed": 20,
|
|
"pending": 0,
|
|
"unique_recipients": 350,
|
|
"total_conversations": 400,
|
|
"delivery_rate": 96.67,
|
|
"period": {
|
|
"start": "2024-01-01T00:00:00",
|
|
"end": "2024-01-31T23:59:59"
|
|
}
|
|
}
|
|
}
|