"""Webhook models for WhatsApp SDK.
These models handle incoming webhook events from WhatsApp,
including messages, status updates, and system notifications.
"""
from __future__ import annotations
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field
# ============================================================================
# WEBHOOK MESSAGE TYPES
# ============================================================================
[docs]
class WebhookTextMessage(BaseModel):
"""Incoming text message."""
body: str = Field(..., description="Text content of the message")
[docs]
class WebhookImageMessage(BaseModel):
"""Incoming image message."""
id: str = Field(..., description="Media ID of the image")
mime_type: str = Field(..., description="MIME type of the image")
sha256: str = Field(..., description="SHA256 hash of the image")
caption: Optional[str] = Field(None, description="Optional caption")
[docs]
class WebhookVideoMessage(BaseModel):
"""Incoming video message."""
id: str = Field(..., description="Media ID of the video")
mime_type: str = Field(..., description="MIME type of the video")
sha256: str = Field(..., description="SHA256 hash of the video")
caption: Optional[str] = Field(None, description="Optional caption")
[docs]
class WebhookAudioMessage(BaseModel):
"""Incoming audio message."""
id: str = Field(..., description="Media ID of the audio")
mime_type: str = Field(..., description="MIME type of the audio")
sha256: str = Field(..., description="SHA256 hash of the audio")
voice: Optional[bool] = Field(None, description="Whether this is a voice message")
[docs]
class WebhookDocumentMessage(BaseModel):
"""Incoming document message."""
id: str = Field(..., description="Media ID of the document")
mime_type: str = Field(..., description="MIME type of the document")
sha256: str = Field(..., description="SHA256 hash of the document")
caption: Optional[str] = Field(None, description="Optional caption")
filename: Optional[str] = Field(None, description="Document filename")
[docs]
class WebhookLocationMessage(BaseModel):
"""Incoming location message."""
latitude: float = Field(..., description="Latitude coordinate")
longitude: float = Field(..., description="Longitude coordinate")
name: Optional[str] = Field(None, description="Location name")
address: Optional[str] = Field(None, description="Location address")
[docs]
class WebhookStickerMessage(BaseModel):
"""Incoming sticker message."""
id: str = Field(..., description="Media ID of the sticker")
mime_type: str = Field(..., description="MIME type of the sticker")
sha256: str = Field(..., description="SHA256 hash of the sticker")
animated: Optional[bool] = Field(None, description="Whether the sticker is animated")
[docs]
class WebhookListReply(BaseModel):
"""List selection from interactive message."""
id: str = Field(..., description="Selected row ID")
title: str = Field(..., description="Selected row title")
description: Optional[str] = Field(None, description="Selected row description")
[docs]
class WebhookInteractiveMessage(BaseModel):
"""Incoming interactive message response."""
type: str = Field(..., description="Interactive type: button_reply or list_reply")
button_reply: Optional[WebhookButtonReply] = Field(None, description="Button reply data")
list_reply: Optional[WebhookListReply] = Field(None, description="List selection data")
[docs]
class WebhookReactionMessage(BaseModel):
"""Incoming reaction message."""
message_id: str = Field(..., description="ID of the message being reacted to")
emoji: str = Field(..., description="Emoji used for reaction")
[docs]
class WebhookContext(BaseModel):
"""Message context (for replies)."""
from_: str = Field(..., alias="from", description="Sender of the original message")
id: str = Field(..., description="ID of the message being replied to")
# ============================================================================
# WEBHOOK MESSAGE WRAPPER
# ============================================================================
[docs]
class WebhookMessage(BaseModel):
"""Complete incoming message from webhook."""
from_: str = Field(..., alias="from", description="Sender's WhatsApp ID")
id: str = Field(..., description="Message ID")
timestamp: str = Field(..., description="Unix timestamp when message was sent")
type: str = Field(
...,
description="Message type: text, image, video, audio, document, location, sticker, interactive, reaction",
)
context: Optional[WebhookContext] = Field(None, description="Context if this is a reply")
text: Optional[WebhookTextMessage] = Field(None, description="Text message data")
image: Optional[WebhookImageMessage] = Field(None, description="Image message data")
video: Optional[WebhookVideoMessage] = Field(None, description="Video message data")
audio: Optional[WebhookAudioMessage] = Field(None, description="Audio message data")
document: Optional[WebhookDocumentMessage] = Field(None, description="Document message data")
location: Optional[WebhookLocationMessage] = Field(None, description="Location message data")
sticker: Optional[WebhookStickerMessage] = Field(None, description="Sticker message data")
interactive: Optional[WebhookInteractiveMessage] = Field(
None, description="Interactive message response data"
)
reaction: Optional[WebhookReactionMessage] = Field(None, description="Reaction message data")
contacts: List[Dict[str, Optional[Any]]] = Field(default_factory=list, description="Contact cards shared")
errors: List[Dict[str, Optional[Any]]] = Field(
default_factory=list, description="Any errors associated with the message"
)
# ============================================================================
# WEBHOOK STATUS UPDATE
# ============================================================================
[docs]
class WebhookStatus(BaseModel):
"""Message status update from webhook."""
id: str = Field(..., description="Message ID")
status: str = Field(..., description="Status: sent, delivered, read, failed")
timestamp: str = Field(..., description="Unix timestamp of status update")
recipient_id: str = Field(..., description="Recipient's WhatsApp ID")
conversation: Dict[str, Optional[Any]] = Field(default_factory=dict, description="Conversation details")
pricing: Dict[str, Optional[Any]] = Field(default_factory=dict, description="Pricing information")
errors: List[Dict[str, Optional[Any]]] = Field(
default_factory=list, description="Error details if status is failed"
)
# ============================================================================
# WEBHOOK METADATA
# ============================================================================
# ============================================================================
# WEBHOOK VALUE (MAIN PAYLOAD)
# ============================================================================
[docs]
class WebhookValue(BaseModel):
"""Main webhook value containing messages or statuses."""
messaging_product: str = Field("whatsapp", description="Always 'whatsapp'")
metadata: WebhookMetadata = Field(..., description="Webhook metadata")
contacts: Optional[List[WebhookContact]] = Field(None, description="Contact information")
messages: Optional[List[WebhookMessage]] = Field(None, description="Incoming messages")
statuses: Optional[List[WebhookStatus]] = Field(None, description="Status updates")
errors: List[Dict[str, Optional[Any]]] = Field(default_factory=list, description="Any errors")
[docs]
class WebhookChange(BaseModel):
"""Webhook change event."""
value: WebhookValue = Field(..., description="Change value")
field: str = Field(..., description="Field that changed (usually 'messages')")
[docs]
class WebhookEntry(BaseModel):
"""Webhook entry containing changes."""
id: str = Field(..., description="Business account ID")
changes: List[WebhookChange] = Field(..., description="List of changes")
[docs]
class WebhookEvent(BaseModel):
"""Complete webhook event from WhatsApp."""
object: str = Field(..., description="Object type (usually 'whatsapp_business_account')")
entry: List[WebhookEntry] = Field(..., description="List of entries")
# ============================================================================
# WEBHOOK VERIFICATION
# ============================================================================
[docs]
class WebhookVerification(BaseModel):
"""Webhook verification request."""
hub_mode: str = Field(..., alias="hub.mode", description="Should be 'subscribe'")
hub_challenge: str = Field(..., alias="hub.challenge", description="Challenge to echo back")
hub_verify_token: str = Field(
..., alias="hub.verify_token", description="Verify token to validate"
)