wappa 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of wappa might be problematic. Click here for more details.
- wappa/__init__.py +85 -0
- wappa/api/__init__.py +1 -0
- wappa/api/controllers/__init__.py +10 -0
- wappa/api/controllers/webhook_controller.py +441 -0
- wappa/api/dependencies/__init__.py +15 -0
- wappa/api/dependencies/whatsapp_dependencies.py +220 -0
- wappa/api/dependencies/whatsapp_media_dependencies.py +26 -0
- wappa/api/middleware/__init__.py +7 -0
- wappa/api/middleware/error_handler.py +158 -0
- wappa/api/middleware/owner.py +99 -0
- wappa/api/middleware/request_logging.py +184 -0
- wappa/api/routes/__init__.py +6 -0
- wappa/api/routes/health.py +102 -0
- wappa/api/routes/webhooks.py +211 -0
- wappa/api/routes/whatsapp/__init__.py +15 -0
- wappa/api/routes/whatsapp/whatsapp_interactive.py +429 -0
- wappa/api/routes/whatsapp/whatsapp_media.py +440 -0
- wappa/api/routes/whatsapp/whatsapp_messages.py +195 -0
- wappa/api/routes/whatsapp/whatsapp_specialized.py +516 -0
- wappa/api/routes/whatsapp/whatsapp_templates.py +431 -0
- wappa/api/routes/whatsapp_combined.py +35 -0
- wappa/cli/__init__.py +9 -0
- wappa/cli/main.py +199 -0
- wappa/core/__init__.py +6 -0
- wappa/core/config/__init__.py +5 -0
- wappa/core/config/settings.py +161 -0
- wappa/core/events/__init__.py +41 -0
- wappa/core/events/default_handlers.py +642 -0
- wappa/core/events/event_dispatcher.py +244 -0
- wappa/core/events/event_handler.py +247 -0
- wappa/core/events/webhook_factory.py +219 -0
- wappa/core/factory/__init__.py +15 -0
- wappa/core/factory/plugin.py +68 -0
- wappa/core/factory/wappa_builder.py +326 -0
- wappa/core/logging/__init__.py +5 -0
- wappa/core/logging/context.py +100 -0
- wappa/core/logging/logger.py +343 -0
- wappa/core/plugins/__init__.py +34 -0
- wappa/core/plugins/auth_plugin.py +169 -0
- wappa/core/plugins/cors_plugin.py +128 -0
- wappa/core/plugins/custom_middleware_plugin.py +182 -0
- wappa/core/plugins/database_plugin.py +235 -0
- wappa/core/plugins/rate_limit_plugin.py +183 -0
- wappa/core/plugins/redis_plugin.py +224 -0
- wappa/core/plugins/wappa_core_plugin.py +261 -0
- wappa/core/plugins/webhook_plugin.py +253 -0
- wappa/core/types.py +108 -0
- wappa/core/wappa_app.py +546 -0
- wappa/database/__init__.py +18 -0
- wappa/database/adapter.py +107 -0
- wappa/database/adapters/__init__.py +17 -0
- wappa/database/adapters/mysql_adapter.py +187 -0
- wappa/database/adapters/postgresql_adapter.py +169 -0
- wappa/database/adapters/sqlite_adapter.py +174 -0
- wappa/domain/__init__.py +28 -0
- wappa/domain/builders/__init__.py +5 -0
- wappa/domain/builders/message_builder.py +189 -0
- wappa/domain/entities/__init__.py +5 -0
- wappa/domain/enums/messenger_platform.py +123 -0
- wappa/domain/factories/__init__.py +6 -0
- wappa/domain/factories/media_factory.py +450 -0
- wappa/domain/factories/message_factory.py +497 -0
- wappa/domain/factories/messenger_factory.py +244 -0
- wappa/domain/interfaces/__init__.py +32 -0
- wappa/domain/interfaces/base_repository.py +94 -0
- wappa/domain/interfaces/cache_factory.py +85 -0
- wappa/domain/interfaces/cache_interface.py +199 -0
- wappa/domain/interfaces/expiry_repository.py +68 -0
- wappa/domain/interfaces/media_interface.py +311 -0
- wappa/domain/interfaces/messaging_interface.py +523 -0
- wappa/domain/interfaces/pubsub_repository.py +151 -0
- wappa/domain/interfaces/repository_factory.py +108 -0
- wappa/domain/interfaces/shared_state_repository.py +122 -0
- wappa/domain/interfaces/state_repository.py +123 -0
- wappa/domain/interfaces/tables_repository.py +215 -0
- wappa/domain/interfaces/user_repository.py +114 -0
- wappa/domain/interfaces/webhooks/__init__.py +1 -0
- wappa/domain/models/media_result.py +110 -0
- wappa/domain/models/platforms/__init__.py +15 -0
- wappa/domain/models/platforms/platform_config.py +104 -0
- wappa/domain/services/__init__.py +11 -0
- wappa/domain/services/tenant_credentials_service.py +56 -0
- wappa/messaging/__init__.py +7 -0
- wappa/messaging/whatsapp/__init__.py +1 -0
- wappa/messaging/whatsapp/client/__init__.py +5 -0
- wappa/messaging/whatsapp/client/whatsapp_client.py +417 -0
- wappa/messaging/whatsapp/handlers/__init__.py +13 -0
- wappa/messaging/whatsapp/handlers/whatsapp_interactive_handler.py +653 -0
- wappa/messaging/whatsapp/handlers/whatsapp_media_handler.py +579 -0
- wappa/messaging/whatsapp/handlers/whatsapp_specialized_handler.py +434 -0
- wappa/messaging/whatsapp/handlers/whatsapp_template_handler.py +416 -0
- wappa/messaging/whatsapp/messenger/__init__.py +5 -0
- wappa/messaging/whatsapp/messenger/whatsapp_messenger.py +904 -0
- wappa/messaging/whatsapp/models/__init__.py +61 -0
- wappa/messaging/whatsapp/models/basic_models.py +65 -0
- wappa/messaging/whatsapp/models/interactive_models.py +287 -0
- wappa/messaging/whatsapp/models/media_models.py +215 -0
- wappa/messaging/whatsapp/models/specialized_models.py +304 -0
- wappa/messaging/whatsapp/models/template_models.py +261 -0
- wappa/persistence/cache_factory.py +93 -0
- wappa/persistence/json/__init__.py +14 -0
- wappa/persistence/json/cache_adapters.py +271 -0
- wappa/persistence/json/handlers/__init__.py +1 -0
- wappa/persistence/json/handlers/state_handler.py +250 -0
- wappa/persistence/json/handlers/table_handler.py +263 -0
- wappa/persistence/json/handlers/user_handler.py +213 -0
- wappa/persistence/json/handlers/utils/__init__.py +1 -0
- wappa/persistence/json/handlers/utils/file_manager.py +153 -0
- wappa/persistence/json/handlers/utils/key_factory.py +11 -0
- wappa/persistence/json/handlers/utils/serialization.py +121 -0
- wappa/persistence/json/json_cache_factory.py +76 -0
- wappa/persistence/json/storage_manager.py +285 -0
- wappa/persistence/memory/__init__.py +14 -0
- wappa/persistence/memory/cache_adapters.py +271 -0
- wappa/persistence/memory/handlers/__init__.py +1 -0
- wappa/persistence/memory/handlers/state_handler.py +250 -0
- wappa/persistence/memory/handlers/table_handler.py +280 -0
- wappa/persistence/memory/handlers/user_handler.py +213 -0
- wappa/persistence/memory/handlers/utils/__init__.py +1 -0
- wappa/persistence/memory/handlers/utils/key_factory.py +11 -0
- wappa/persistence/memory/handlers/utils/memory_store.py +317 -0
- wappa/persistence/memory/handlers/utils/ttl_manager.py +235 -0
- wappa/persistence/memory/memory_cache_factory.py +76 -0
- wappa/persistence/memory/storage_manager.py +235 -0
- wappa/persistence/redis/README.md +699 -0
- wappa/persistence/redis/__init__.py +11 -0
- wappa/persistence/redis/cache_adapters.py +285 -0
- wappa/persistence/redis/ops.py +880 -0
- wappa/persistence/redis/redis_cache_factory.py +71 -0
- wappa/persistence/redis/redis_client.py +231 -0
- wappa/persistence/redis/redis_handler/__init__.py +26 -0
- wappa/persistence/redis/redis_handler/state_handler.py +176 -0
- wappa/persistence/redis/redis_handler/table.py +158 -0
- wappa/persistence/redis/redis_handler/user.py +138 -0
- wappa/persistence/redis/redis_handler/utils/__init__.py +12 -0
- wappa/persistence/redis/redis_handler/utils/key_factory.py +32 -0
- wappa/persistence/redis/redis_handler/utils/serde.py +146 -0
- wappa/persistence/redis/redis_handler/utils/tenant_cache.py +268 -0
- wappa/persistence/redis/redis_manager.py +189 -0
- wappa/processors/__init__.py +6 -0
- wappa/processors/base_processor.py +262 -0
- wappa/processors/factory.py +550 -0
- wappa/processors/whatsapp_processor.py +810 -0
- wappa/schemas/__init__.py +6 -0
- wappa/schemas/core/__init__.py +71 -0
- wappa/schemas/core/base_message.py +499 -0
- wappa/schemas/core/base_status.py +322 -0
- wappa/schemas/core/base_webhook.py +312 -0
- wappa/schemas/core/types.py +253 -0
- wappa/schemas/core/webhook_interfaces/__init__.py +48 -0
- wappa/schemas/core/webhook_interfaces/base_components.py +293 -0
- wappa/schemas/core/webhook_interfaces/universal_webhooks.py +348 -0
- wappa/schemas/factory.py +754 -0
- wappa/schemas/webhooks/__init__.py +3 -0
- wappa/schemas/whatsapp/__init__.py +6 -0
- wappa/schemas/whatsapp/base_models.py +285 -0
- wappa/schemas/whatsapp/message_types/__init__.py +93 -0
- wappa/schemas/whatsapp/message_types/audio.py +350 -0
- wappa/schemas/whatsapp/message_types/button.py +267 -0
- wappa/schemas/whatsapp/message_types/contact.py +464 -0
- wappa/schemas/whatsapp/message_types/document.py +421 -0
- wappa/schemas/whatsapp/message_types/errors.py +195 -0
- wappa/schemas/whatsapp/message_types/image.py +424 -0
- wappa/schemas/whatsapp/message_types/interactive.py +430 -0
- wappa/schemas/whatsapp/message_types/location.py +416 -0
- wappa/schemas/whatsapp/message_types/order.py +372 -0
- wappa/schemas/whatsapp/message_types/reaction.py +271 -0
- wappa/schemas/whatsapp/message_types/sticker.py +328 -0
- wappa/schemas/whatsapp/message_types/system.py +317 -0
- wappa/schemas/whatsapp/message_types/text.py +411 -0
- wappa/schemas/whatsapp/message_types/unsupported.py +273 -0
- wappa/schemas/whatsapp/message_types/video.py +344 -0
- wappa/schemas/whatsapp/status_models.py +479 -0
- wappa/schemas/whatsapp/validators.py +454 -0
- wappa/schemas/whatsapp/webhook_container.py +438 -0
- wappa/webhooks/__init__.py +17 -0
- wappa/webhooks/core/__init__.py +71 -0
- wappa/webhooks/core/base_message.py +499 -0
- wappa/webhooks/core/base_status.py +322 -0
- wappa/webhooks/core/base_webhook.py +312 -0
- wappa/webhooks/core/types.py +253 -0
- wappa/webhooks/core/webhook_interfaces/__init__.py +48 -0
- wappa/webhooks/core/webhook_interfaces/base_components.py +293 -0
- wappa/webhooks/core/webhook_interfaces/universal_webhooks.py +441 -0
- wappa/webhooks/factory.py +754 -0
- wappa/webhooks/whatsapp/__init__.py +6 -0
- wappa/webhooks/whatsapp/base_models.py +285 -0
- wappa/webhooks/whatsapp/message_types/__init__.py +93 -0
- wappa/webhooks/whatsapp/message_types/audio.py +350 -0
- wappa/webhooks/whatsapp/message_types/button.py +267 -0
- wappa/webhooks/whatsapp/message_types/contact.py +464 -0
- wappa/webhooks/whatsapp/message_types/document.py +421 -0
- wappa/webhooks/whatsapp/message_types/errors.py +195 -0
- wappa/webhooks/whatsapp/message_types/image.py +424 -0
- wappa/webhooks/whatsapp/message_types/interactive.py +430 -0
- wappa/webhooks/whatsapp/message_types/location.py +416 -0
- wappa/webhooks/whatsapp/message_types/order.py +372 -0
- wappa/webhooks/whatsapp/message_types/reaction.py +271 -0
- wappa/webhooks/whatsapp/message_types/sticker.py +328 -0
- wappa/webhooks/whatsapp/message_types/system.py +317 -0
- wappa/webhooks/whatsapp/message_types/text.py +411 -0
- wappa/webhooks/whatsapp/message_types/unsupported.py +273 -0
- wappa/webhooks/whatsapp/message_types/video.py +344 -0
- wappa/webhooks/whatsapp/status_models.py +479 -0
- wappa/webhooks/whatsapp/validators.py +454 -0
- wappa/webhooks/whatsapp/webhook_container.py +438 -0
- wappa-0.1.0.dist-info/METADATA +269 -0
- wappa-0.1.0.dist-info/RECORD +211 -0
- wappa-0.1.0.dist-info/WHEEL +4 -0
- wappa-0.1.0.dist-info/entry_points.txt +2 -0
- wappa-0.1.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base models for WhatsApp Business Platform webhooks.
|
|
3
|
+
|
|
4
|
+
This module contains common Pydantic models used across different WhatsApp
|
|
5
|
+
message types, including metadata, contact information, and context models.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Literal
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class WhatsAppMetadata(BaseModel):
|
|
14
|
+
"""
|
|
15
|
+
Business phone number metadata from WhatsApp webhooks.
|
|
16
|
+
|
|
17
|
+
Present in all webhook payloads to identify the business phone number
|
|
18
|
+
that received or sent the message.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
model_config = ConfigDict(
|
|
22
|
+
extra="forbid", str_strip_whitespace=True, validate_assignment=True
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
display_phone_number: str = Field(
|
|
26
|
+
..., description="Business display phone number (formatted for display)"
|
|
27
|
+
)
|
|
28
|
+
phone_number_id: str = Field(
|
|
29
|
+
..., description="Business phone number ID (WhatsApp internal identifier)"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
@field_validator("display_phone_number")
|
|
33
|
+
@classmethod
|
|
34
|
+
def validate_display_phone_number(cls, v: str) -> str:
|
|
35
|
+
"""Validate display phone number format."""
|
|
36
|
+
if not v or len(v) < 10:
|
|
37
|
+
raise ValueError("Display phone number must be at least 10 characters")
|
|
38
|
+
return v
|
|
39
|
+
|
|
40
|
+
@field_validator("phone_number_id")
|
|
41
|
+
@classmethod
|
|
42
|
+
def validate_phone_number_id(cls, v: str) -> str:
|
|
43
|
+
"""Validate phone number ID format."""
|
|
44
|
+
if not v or not v.isdigit():
|
|
45
|
+
raise ValueError("Phone number ID must be numeric")
|
|
46
|
+
return v
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ContactProfile(BaseModel):
|
|
50
|
+
"""User profile information from WhatsApp contact."""
|
|
51
|
+
|
|
52
|
+
model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
|
|
53
|
+
|
|
54
|
+
name: str = Field(..., description="WhatsApp user's display name")
|
|
55
|
+
|
|
56
|
+
@field_validator("name")
|
|
57
|
+
@classmethod
|
|
58
|
+
def validate_name(cls, v: str) -> str:
|
|
59
|
+
"""Validate user name is not empty."""
|
|
60
|
+
if not v.strip():
|
|
61
|
+
raise ValueError("Contact name cannot be empty")
|
|
62
|
+
return v.strip()
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class WhatsAppContact(BaseModel):
|
|
66
|
+
"""
|
|
67
|
+
Contact information for WhatsApp users.
|
|
68
|
+
|
|
69
|
+
Present in incoming message webhooks to identify the sender.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
|
|
73
|
+
|
|
74
|
+
wa_id: str = Field(
|
|
75
|
+
..., description="WhatsApp user ID (may differ from phone number)"
|
|
76
|
+
)
|
|
77
|
+
profile: ContactProfile = Field(..., description="User profile information")
|
|
78
|
+
identity_key_hash: str | None = Field(
|
|
79
|
+
None, description="Identity key hash (only if identity change check enabled)"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
@field_validator("wa_id")
|
|
83
|
+
@classmethod
|
|
84
|
+
def validate_wa_id(cls, v: str) -> str:
|
|
85
|
+
"""Validate WhatsApp ID format."""
|
|
86
|
+
if not v or len(v) < 8:
|
|
87
|
+
raise ValueError("WhatsApp ID must be at least 8 characters")
|
|
88
|
+
return v
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class ReferredProduct(BaseModel):
|
|
92
|
+
"""Product catalog reference for message business button context."""
|
|
93
|
+
|
|
94
|
+
model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
|
|
95
|
+
|
|
96
|
+
catalog_id: str = Field(..., description="Product catalog ID")
|
|
97
|
+
product_retailer_id: str = Field(..., description="Product retailer ID")
|
|
98
|
+
|
|
99
|
+
@field_validator("catalog_id", "product_retailer_id")
|
|
100
|
+
@classmethod
|
|
101
|
+
def validate_ids(cls, v: str) -> str:
|
|
102
|
+
"""Validate IDs are not empty."""
|
|
103
|
+
if not v.strip():
|
|
104
|
+
raise ValueError("Product IDs cannot be empty")
|
|
105
|
+
return v.strip()
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class MessageContext(BaseModel):
|
|
109
|
+
"""
|
|
110
|
+
Context information for WhatsApp messages.
|
|
111
|
+
|
|
112
|
+
Used for replies, forwards, and message business button interactions.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
|
|
116
|
+
|
|
117
|
+
# For replies and message business buttons
|
|
118
|
+
from_: str | None = Field(
|
|
119
|
+
None, alias="from", description="Original message sender (for replies)"
|
|
120
|
+
)
|
|
121
|
+
id: str | None = Field(
|
|
122
|
+
None, description="ID of the original message being replied to or referenced"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# For forwarded messages
|
|
126
|
+
forwarded: bool | None = Field(
|
|
127
|
+
None, description="True if forwarded 5 times or less"
|
|
128
|
+
)
|
|
129
|
+
frequently_forwarded: bool | None = Field(
|
|
130
|
+
None, description="True if forwarded more than 5 times"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# For message business button context
|
|
134
|
+
referred_product: ReferredProduct | None = Field(
|
|
135
|
+
None, description="Product information for message business button"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
@field_validator("id")
|
|
139
|
+
@classmethod
|
|
140
|
+
def validate_message_id(cls, v: str | None) -> str | None:
|
|
141
|
+
"""Validate message ID format if present."""
|
|
142
|
+
if v is not None and not v.strip():
|
|
143
|
+
raise ValueError("Message ID cannot be empty string")
|
|
144
|
+
return v
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class WelcomeMessage(BaseModel):
|
|
148
|
+
"""Welcome message for Click-to-WhatsApp ads."""
|
|
149
|
+
|
|
150
|
+
model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
|
|
151
|
+
|
|
152
|
+
text: str = Field(..., description="Ad greeting text")
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class AdReferral(BaseModel):
|
|
156
|
+
"""
|
|
157
|
+
Click-to-WhatsApp ad referral information.
|
|
158
|
+
|
|
159
|
+
Present when a user sends a message via a Click-to-WhatsApp ad.
|
|
160
|
+
"""
|
|
161
|
+
|
|
162
|
+
model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
|
|
163
|
+
|
|
164
|
+
source_url: str = Field(..., description="Click-to-WhatsApp ad URL")
|
|
165
|
+
source_id: str = Field(..., description="Click-to-WhatsApp ad ID")
|
|
166
|
+
source_type: Literal["ad"] = Field(..., description="Source type (always 'ad')")
|
|
167
|
+
body: str = Field(..., description="Ad primary text")
|
|
168
|
+
headline: str = Field(..., description="Ad headline")
|
|
169
|
+
media_type: Literal["image", "video"] = Field(..., description="Ad media type")
|
|
170
|
+
|
|
171
|
+
# Media URLs (conditionally present based on media_type)
|
|
172
|
+
image_url: str | None = Field(None, description="Ad image URL (only for image ads)")
|
|
173
|
+
video_url: str | None = Field(None, description="Ad video URL (only for video ads)")
|
|
174
|
+
thumbnail_url: str | None = Field(
|
|
175
|
+
None, description="Ad video thumbnail URL (only for video ads)"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
ctwa_clid: str = Field(..., description="Click-to-WhatsApp ad click ID")
|
|
179
|
+
welcome_message: WelcomeMessage = Field(..., description="Ad greeting message")
|
|
180
|
+
|
|
181
|
+
@field_validator("source_url", "image_url", "video_url", "thumbnail_url")
|
|
182
|
+
@classmethod
|
|
183
|
+
def validate_urls(cls, v: str | None) -> str | None:
|
|
184
|
+
"""Validate URL format if present."""
|
|
185
|
+
if v is not None:
|
|
186
|
+
if not v.strip():
|
|
187
|
+
return None
|
|
188
|
+
if not (v.startswith("http://") or v.startswith("https://")):
|
|
189
|
+
raise ValueError("URLs must start with http:// or https://")
|
|
190
|
+
return v
|
|
191
|
+
|
|
192
|
+
@field_validator("source_id", "ctwa_clid")
|
|
193
|
+
@classmethod
|
|
194
|
+
def validate_ids(cls, v: str) -> str:
|
|
195
|
+
"""Validate required IDs are not empty."""
|
|
196
|
+
if not v.strip():
|
|
197
|
+
raise ValueError("Ad IDs cannot be empty")
|
|
198
|
+
return v.strip()
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class ConversationOrigin(BaseModel):
|
|
202
|
+
"""Conversation origin information for pricing."""
|
|
203
|
+
|
|
204
|
+
model_config = ConfigDict(extra="forbid")
|
|
205
|
+
|
|
206
|
+
type: Literal[
|
|
207
|
+
"authentication",
|
|
208
|
+
"authentication_international",
|
|
209
|
+
"marketing",
|
|
210
|
+
"marketing_lite",
|
|
211
|
+
"referral_conversion",
|
|
212
|
+
"service",
|
|
213
|
+
"utility",
|
|
214
|
+
] = Field(..., description="Conversation category")
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class Conversation(BaseModel):
|
|
218
|
+
"""Conversation information for message status."""
|
|
219
|
+
|
|
220
|
+
model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
|
|
221
|
+
|
|
222
|
+
id: str = Field(..., description="Conversation ID")
|
|
223
|
+
expiration_timestamp: str | None = Field(
|
|
224
|
+
None, description="Unix timestamp when conversation expires"
|
|
225
|
+
)
|
|
226
|
+
origin: ConversationOrigin = Field(..., description="Conversation origin")
|
|
227
|
+
|
|
228
|
+
@field_validator("expiration_timestamp")
|
|
229
|
+
@classmethod
|
|
230
|
+
def validate_timestamp(cls, v: str | None) -> str | None:
|
|
231
|
+
"""Validate timestamp format if present."""
|
|
232
|
+
if v is not None and not v.isdigit():
|
|
233
|
+
raise ValueError("Timestamp must be numeric")
|
|
234
|
+
return v
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class Pricing(BaseModel):
|
|
238
|
+
"""Pricing information for message status."""
|
|
239
|
+
|
|
240
|
+
model_config = ConfigDict(extra="forbid")
|
|
241
|
+
|
|
242
|
+
billable: bool = Field(..., description="Whether message is billable")
|
|
243
|
+
pricing_model: Literal["CBP", "PMP"] = Field(
|
|
244
|
+
..., description="Pricing model (CBP=conversation-based, PMP=per-message)"
|
|
245
|
+
)
|
|
246
|
+
type: Literal["regular", "free_customer_service", "free_entry_point"] | None = (
|
|
247
|
+
Field(None, description="Pricing type (available from July 1, 2025)")
|
|
248
|
+
)
|
|
249
|
+
category: Literal[
|
|
250
|
+
"authentication",
|
|
251
|
+
"authentication-international",
|
|
252
|
+
"marketing",
|
|
253
|
+
"marketing_lite",
|
|
254
|
+
"referral_conversion",
|
|
255
|
+
"service",
|
|
256
|
+
"utility",
|
|
257
|
+
] = Field(..., description="Pricing category")
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
class ErrorData(BaseModel):
|
|
261
|
+
"""Error details for failed messages."""
|
|
262
|
+
|
|
263
|
+
model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
|
|
264
|
+
|
|
265
|
+
details: str = Field(..., description="Detailed error description")
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
class MessageError(BaseModel):
|
|
269
|
+
"""Error information for failed messages."""
|
|
270
|
+
|
|
271
|
+
model_config = ConfigDict(extra="forbid", str_strip_whitespace=True)
|
|
272
|
+
|
|
273
|
+
code: int = Field(..., description="Error code")
|
|
274
|
+
title: str = Field(..., description="Error title")
|
|
275
|
+
message: str = Field(..., description="Error message")
|
|
276
|
+
error_data: ErrorData = Field(..., description="Additional error details")
|
|
277
|
+
href: str | None = Field(None, description="Link to error documentation (optional)")
|
|
278
|
+
|
|
279
|
+
@field_validator("href")
|
|
280
|
+
@classmethod
|
|
281
|
+
def validate_href(cls, v: str | None) -> str | None:
|
|
282
|
+
"""Validate error documentation URL."""
|
|
283
|
+
if v is not None and not v.startswith("https://"):
|
|
284
|
+
raise ValueError("Error documentation URL must use HTTPS")
|
|
285
|
+
return v
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
WhatsApp message type schemas.
|
|
3
|
+
|
|
4
|
+
This package contains specific Pydantic models for each WhatsApp message type
|
|
5
|
+
including text, interactive, media, contact, location, and system messages.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# Import all message type schemas
|
|
9
|
+
from .audio import AudioContent, WhatsAppAudioMessage
|
|
10
|
+
from .button import ButtonContent, WhatsAppButtonMessage
|
|
11
|
+
from .contact import (
|
|
12
|
+
ContactAddress,
|
|
13
|
+
ContactEmail,
|
|
14
|
+
ContactInfo,
|
|
15
|
+
ContactName,
|
|
16
|
+
ContactOrganization,
|
|
17
|
+
ContactPhone,
|
|
18
|
+
ContactUrl,
|
|
19
|
+
WhatsAppContactMessage,
|
|
20
|
+
)
|
|
21
|
+
from .document import DocumentContent, WhatsAppDocumentMessage
|
|
22
|
+
from .errors import WhatsAppWebhookError, create_webhook_error_from_raw
|
|
23
|
+
from .image import ImageContent, WhatsAppImageMessage
|
|
24
|
+
from .interactive import (
|
|
25
|
+
ButtonReply,
|
|
26
|
+
InteractiveContent,
|
|
27
|
+
ListReply,
|
|
28
|
+
WhatsAppInteractiveMessage,
|
|
29
|
+
)
|
|
30
|
+
from .location import LocationContent, WhatsAppLocationMessage
|
|
31
|
+
from .order import OrderContent, OrderProductItem, WhatsAppOrderMessage
|
|
32
|
+
from .reaction import ReactionContent, WhatsAppReactionMessage
|
|
33
|
+
from .sticker import StickerContent, WhatsAppStickerMessage
|
|
34
|
+
from .system import SystemContent, WhatsAppSystemMessage
|
|
35
|
+
from .text import TextContent, WhatsAppTextMessage
|
|
36
|
+
from .unsupported import WhatsAppUnsupportedMessage
|
|
37
|
+
from .video import VideoContent, WhatsAppVideoMessage
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
# Audio message schemas
|
|
41
|
+
"AudioContent",
|
|
42
|
+
"WhatsAppAudioMessage",
|
|
43
|
+
# Button message schemas
|
|
44
|
+
"ButtonContent",
|
|
45
|
+
"WhatsAppButtonMessage",
|
|
46
|
+
# Contact message schemas
|
|
47
|
+
"ContactAddress",
|
|
48
|
+
"ContactEmail",
|
|
49
|
+
"ContactName",
|
|
50
|
+
"ContactOrganization",
|
|
51
|
+
"ContactPhone",
|
|
52
|
+
"ContactUrl",
|
|
53
|
+
"ContactInfo",
|
|
54
|
+
"WhatsAppContactMessage",
|
|
55
|
+
# Document message schemas
|
|
56
|
+
"DocumentContent",
|
|
57
|
+
"WhatsAppDocumentMessage",
|
|
58
|
+
# Error schemas (webhook-level)
|
|
59
|
+
"WhatsAppWebhookError",
|
|
60
|
+
"create_webhook_error_from_raw",
|
|
61
|
+
# Image message schemas
|
|
62
|
+
"ImageContent",
|
|
63
|
+
"WhatsAppImageMessage",
|
|
64
|
+
# Interactive message schemas
|
|
65
|
+
"ButtonReply",
|
|
66
|
+
"ListReply",
|
|
67
|
+
"InteractiveContent",
|
|
68
|
+
"WhatsAppInteractiveMessage",
|
|
69
|
+
# Location message schemas
|
|
70
|
+
"LocationContent",
|
|
71
|
+
"WhatsAppLocationMessage",
|
|
72
|
+
# Order message schemas
|
|
73
|
+
"OrderProductItem",
|
|
74
|
+
"OrderContent",
|
|
75
|
+
"WhatsAppOrderMessage",
|
|
76
|
+
# Reaction message schemas
|
|
77
|
+
"ReactionContent",
|
|
78
|
+
"WhatsAppReactionMessage",
|
|
79
|
+
# Sticker message schemas
|
|
80
|
+
"StickerContent",
|
|
81
|
+
"WhatsAppStickerMessage",
|
|
82
|
+
# System message schemas
|
|
83
|
+
"SystemContent",
|
|
84
|
+
"WhatsAppSystemMessage",
|
|
85
|
+
# Text message schemas
|
|
86
|
+
"TextContent",
|
|
87
|
+
"WhatsAppTextMessage",
|
|
88
|
+
# Unsupported message schemas
|
|
89
|
+
"WhatsAppUnsupportedMessage",
|
|
90
|
+
# Video message schemas
|
|
91
|
+
"VideoContent",
|
|
92
|
+
"WhatsAppVideoMessage",
|
|
93
|
+
]
|