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.

Files changed (211) hide show
  1. wappa/__init__.py +85 -0
  2. wappa/api/__init__.py +1 -0
  3. wappa/api/controllers/__init__.py +10 -0
  4. wappa/api/controllers/webhook_controller.py +441 -0
  5. wappa/api/dependencies/__init__.py +15 -0
  6. wappa/api/dependencies/whatsapp_dependencies.py +220 -0
  7. wappa/api/dependencies/whatsapp_media_dependencies.py +26 -0
  8. wappa/api/middleware/__init__.py +7 -0
  9. wappa/api/middleware/error_handler.py +158 -0
  10. wappa/api/middleware/owner.py +99 -0
  11. wappa/api/middleware/request_logging.py +184 -0
  12. wappa/api/routes/__init__.py +6 -0
  13. wappa/api/routes/health.py +102 -0
  14. wappa/api/routes/webhooks.py +211 -0
  15. wappa/api/routes/whatsapp/__init__.py +15 -0
  16. wappa/api/routes/whatsapp/whatsapp_interactive.py +429 -0
  17. wappa/api/routes/whatsapp/whatsapp_media.py +440 -0
  18. wappa/api/routes/whatsapp/whatsapp_messages.py +195 -0
  19. wappa/api/routes/whatsapp/whatsapp_specialized.py +516 -0
  20. wappa/api/routes/whatsapp/whatsapp_templates.py +431 -0
  21. wappa/api/routes/whatsapp_combined.py +35 -0
  22. wappa/cli/__init__.py +9 -0
  23. wappa/cli/main.py +199 -0
  24. wappa/core/__init__.py +6 -0
  25. wappa/core/config/__init__.py +5 -0
  26. wappa/core/config/settings.py +161 -0
  27. wappa/core/events/__init__.py +41 -0
  28. wappa/core/events/default_handlers.py +642 -0
  29. wappa/core/events/event_dispatcher.py +244 -0
  30. wappa/core/events/event_handler.py +247 -0
  31. wappa/core/events/webhook_factory.py +219 -0
  32. wappa/core/factory/__init__.py +15 -0
  33. wappa/core/factory/plugin.py +68 -0
  34. wappa/core/factory/wappa_builder.py +326 -0
  35. wappa/core/logging/__init__.py +5 -0
  36. wappa/core/logging/context.py +100 -0
  37. wappa/core/logging/logger.py +343 -0
  38. wappa/core/plugins/__init__.py +34 -0
  39. wappa/core/plugins/auth_plugin.py +169 -0
  40. wappa/core/plugins/cors_plugin.py +128 -0
  41. wappa/core/plugins/custom_middleware_plugin.py +182 -0
  42. wappa/core/plugins/database_plugin.py +235 -0
  43. wappa/core/plugins/rate_limit_plugin.py +183 -0
  44. wappa/core/plugins/redis_plugin.py +224 -0
  45. wappa/core/plugins/wappa_core_plugin.py +261 -0
  46. wappa/core/plugins/webhook_plugin.py +253 -0
  47. wappa/core/types.py +108 -0
  48. wappa/core/wappa_app.py +546 -0
  49. wappa/database/__init__.py +18 -0
  50. wappa/database/adapter.py +107 -0
  51. wappa/database/adapters/__init__.py +17 -0
  52. wappa/database/adapters/mysql_adapter.py +187 -0
  53. wappa/database/adapters/postgresql_adapter.py +169 -0
  54. wappa/database/adapters/sqlite_adapter.py +174 -0
  55. wappa/domain/__init__.py +28 -0
  56. wappa/domain/builders/__init__.py +5 -0
  57. wappa/domain/builders/message_builder.py +189 -0
  58. wappa/domain/entities/__init__.py +5 -0
  59. wappa/domain/enums/messenger_platform.py +123 -0
  60. wappa/domain/factories/__init__.py +6 -0
  61. wappa/domain/factories/media_factory.py +450 -0
  62. wappa/domain/factories/message_factory.py +497 -0
  63. wappa/domain/factories/messenger_factory.py +244 -0
  64. wappa/domain/interfaces/__init__.py +32 -0
  65. wappa/domain/interfaces/base_repository.py +94 -0
  66. wappa/domain/interfaces/cache_factory.py +85 -0
  67. wappa/domain/interfaces/cache_interface.py +199 -0
  68. wappa/domain/interfaces/expiry_repository.py +68 -0
  69. wappa/domain/interfaces/media_interface.py +311 -0
  70. wappa/domain/interfaces/messaging_interface.py +523 -0
  71. wappa/domain/interfaces/pubsub_repository.py +151 -0
  72. wappa/domain/interfaces/repository_factory.py +108 -0
  73. wappa/domain/interfaces/shared_state_repository.py +122 -0
  74. wappa/domain/interfaces/state_repository.py +123 -0
  75. wappa/domain/interfaces/tables_repository.py +215 -0
  76. wappa/domain/interfaces/user_repository.py +114 -0
  77. wappa/domain/interfaces/webhooks/__init__.py +1 -0
  78. wappa/domain/models/media_result.py +110 -0
  79. wappa/domain/models/platforms/__init__.py +15 -0
  80. wappa/domain/models/platforms/platform_config.py +104 -0
  81. wappa/domain/services/__init__.py +11 -0
  82. wappa/domain/services/tenant_credentials_service.py +56 -0
  83. wappa/messaging/__init__.py +7 -0
  84. wappa/messaging/whatsapp/__init__.py +1 -0
  85. wappa/messaging/whatsapp/client/__init__.py +5 -0
  86. wappa/messaging/whatsapp/client/whatsapp_client.py +417 -0
  87. wappa/messaging/whatsapp/handlers/__init__.py +13 -0
  88. wappa/messaging/whatsapp/handlers/whatsapp_interactive_handler.py +653 -0
  89. wappa/messaging/whatsapp/handlers/whatsapp_media_handler.py +579 -0
  90. wappa/messaging/whatsapp/handlers/whatsapp_specialized_handler.py +434 -0
  91. wappa/messaging/whatsapp/handlers/whatsapp_template_handler.py +416 -0
  92. wappa/messaging/whatsapp/messenger/__init__.py +5 -0
  93. wappa/messaging/whatsapp/messenger/whatsapp_messenger.py +904 -0
  94. wappa/messaging/whatsapp/models/__init__.py +61 -0
  95. wappa/messaging/whatsapp/models/basic_models.py +65 -0
  96. wappa/messaging/whatsapp/models/interactive_models.py +287 -0
  97. wappa/messaging/whatsapp/models/media_models.py +215 -0
  98. wappa/messaging/whatsapp/models/specialized_models.py +304 -0
  99. wappa/messaging/whatsapp/models/template_models.py +261 -0
  100. wappa/persistence/cache_factory.py +93 -0
  101. wappa/persistence/json/__init__.py +14 -0
  102. wappa/persistence/json/cache_adapters.py +271 -0
  103. wappa/persistence/json/handlers/__init__.py +1 -0
  104. wappa/persistence/json/handlers/state_handler.py +250 -0
  105. wappa/persistence/json/handlers/table_handler.py +263 -0
  106. wappa/persistence/json/handlers/user_handler.py +213 -0
  107. wappa/persistence/json/handlers/utils/__init__.py +1 -0
  108. wappa/persistence/json/handlers/utils/file_manager.py +153 -0
  109. wappa/persistence/json/handlers/utils/key_factory.py +11 -0
  110. wappa/persistence/json/handlers/utils/serialization.py +121 -0
  111. wappa/persistence/json/json_cache_factory.py +76 -0
  112. wappa/persistence/json/storage_manager.py +285 -0
  113. wappa/persistence/memory/__init__.py +14 -0
  114. wappa/persistence/memory/cache_adapters.py +271 -0
  115. wappa/persistence/memory/handlers/__init__.py +1 -0
  116. wappa/persistence/memory/handlers/state_handler.py +250 -0
  117. wappa/persistence/memory/handlers/table_handler.py +280 -0
  118. wappa/persistence/memory/handlers/user_handler.py +213 -0
  119. wappa/persistence/memory/handlers/utils/__init__.py +1 -0
  120. wappa/persistence/memory/handlers/utils/key_factory.py +11 -0
  121. wappa/persistence/memory/handlers/utils/memory_store.py +317 -0
  122. wappa/persistence/memory/handlers/utils/ttl_manager.py +235 -0
  123. wappa/persistence/memory/memory_cache_factory.py +76 -0
  124. wappa/persistence/memory/storage_manager.py +235 -0
  125. wappa/persistence/redis/README.md +699 -0
  126. wappa/persistence/redis/__init__.py +11 -0
  127. wappa/persistence/redis/cache_adapters.py +285 -0
  128. wappa/persistence/redis/ops.py +880 -0
  129. wappa/persistence/redis/redis_cache_factory.py +71 -0
  130. wappa/persistence/redis/redis_client.py +231 -0
  131. wappa/persistence/redis/redis_handler/__init__.py +26 -0
  132. wappa/persistence/redis/redis_handler/state_handler.py +176 -0
  133. wappa/persistence/redis/redis_handler/table.py +158 -0
  134. wappa/persistence/redis/redis_handler/user.py +138 -0
  135. wappa/persistence/redis/redis_handler/utils/__init__.py +12 -0
  136. wappa/persistence/redis/redis_handler/utils/key_factory.py +32 -0
  137. wappa/persistence/redis/redis_handler/utils/serde.py +146 -0
  138. wappa/persistence/redis/redis_handler/utils/tenant_cache.py +268 -0
  139. wappa/persistence/redis/redis_manager.py +189 -0
  140. wappa/processors/__init__.py +6 -0
  141. wappa/processors/base_processor.py +262 -0
  142. wappa/processors/factory.py +550 -0
  143. wappa/processors/whatsapp_processor.py +810 -0
  144. wappa/schemas/__init__.py +6 -0
  145. wappa/schemas/core/__init__.py +71 -0
  146. wappa/schemas/core/base_message.py +499 -0
  147. wappa/schemas/core/base_status.py +322 -0
  148. wappa/schemas/core/base_webhook.py +312 -0
  149. wappa/schemas/core/types.py +253 -0
  150. wappa/schemas/core/webhook_interfaces/__init__.py +48 -0
  151. wappa/schemas/core/webhook_interfaces/base_components.py +293 -0
  152. wappa/schemas/core/webhook_interfaces/universal_webhooks.py +348 -0
  153. wappa/schemas/factory.py +754 -0
  154. wappa/schemas/webhooks/__init__.py +3 -0
  155. wappa/schemas/whatsapp/__init__.py +6 -0
  156. wappa/schemas/whatsapp/base_models.py +285 -0
  157. wappa/schemas/whatsapp/message_types/__init__.py +93 -0
  158. wappa/schemas/whatsapp/message_types/audio.py +350 -0
  159. wappa/schemas/whatsapp/message_types/button.py +267 -0
  160. wappa/schemas/whatsapp/message_types/contact.py +464 -0
  161. wappa/schemas/whatsapp/message_types/document.py +421 -0
  162. wappa/schemas/whatsapp/message_types/errors.py +195 -0
  163. wappa/schemas/whatsapp/message_types/image.py +424 -0
  164. wappa/schemas/whatsapp/message_types/interactive.py +430 -0
  165. wappa/schemas/whatsapp/message_types/location.py +416 -0
  166. wappa/schemas/whatsapp/message_types/order.py +372 -0
  167. wappa/schemas/whatsapp/message_types/reaction.py +271 -0
  168. wappa/schemas/whatsapp/message_types/sticker.py +328 -0
  169. wappa/schemas/whatsapp/message_types/system.py +317 -0
  170. wappa/schemas/whatsapp/message_types/text.py +411 -0
  171. wappa/schemas/whatsapp/message_types/unsupported.py +273 -0
  172. wappa/schemas/whatsapp/message_types/video.py +344 -0
  173. wappa/schemas/whatsapp/status_models.py +479 -0
  174. wappa/schemas/whatsapp/validators.py +454 -0
  175. wappa/schemas/whatsapp/webhook_container.py +438 -0
  176. wappa/webhooks/__init__.py +17 -0
  177. wappa/webhooks/core/__init__.py +71 -0
  178. wappa/webhooks/core/base_message.py +499 -0
  179. wappa/webhooks/core/base_status.py +322 -0
  180. wappa/webhooks/core/base_webhook.py +312 -0
  181. wappa/webhooks/core/types.py +253 -0
  182. wappa/webhooks/core/webhook_interfaces/__init__.py +48 -0
  183. wappa/webhooks/core/webhook_interfaces/base_components.py +293 -0
  184. wappa/webhooks/core/webhook_interfaces/universal_webhooks.py +441 -0
  185. wappa/webhooks/factory.py +754 -0
  186. wappa/webhooks/whatsapp/__init__.py +6 -0
  187. wappa/webhooks/whatsapp/base_models.py +285 -0
  188. wappa/webhooks/whatsapp/message_types/__init__.py +93 -0
  189. wappa/webhooks/whatsapp/message_types/audio.py +350 -0
  190. wappa/webhooks/whatsapp/message_types/button.py +267 -0
  191. wappa/webhooks/whatsapp/message_types/contact.py +464 -0
  192. wappa/webhooks/whatsapp/message_types/document.py +421 -0
  193. wappa/webhooks/whatsapp/message_types/errors.py +195 -0
  194. wappa/webhooks/whatsapp/message_types/image.py +424 -0
  195. wappa/webhooks/whatsapp/message_types/interactive.py +430 -0
  196. wappa/webhooks/whatsapp/message_types/location.py +416 -0
  197. wappa/webhooks/whatsapp/message_types/order.py +372 -0
  198. wappa/webhooks/whatsapp/message_types/reaction.py +271 -0
  199. wappa/webhooks/whatsapp/message_types/sticker.py +328 -0
  200. wappa/webhooks/whatsapp/message_types/system.py +317 -0
  201. wappa/webhooks/whatsapp/message_types/text.py +411 -0
  202. wappa/webhooks/whatsapp/message_types/unsupported.py +273 -0
  203. wappa/webhooks/whatsapp/message_types/video.py +344 -0
  204. wappa/webhooks/whatsapp/status_models.py +479 -0
  205. wappa/webhooks/whatsapp/validators.py +454 -0
  206. wappa/webhooks/whatsapp/webhook_container.py +438 -0
  207. wappa-0.1.0.dist-info/METADATA +269 -0
  208. wappa-0.1.0.dist-info/RECORD +211 -0
  209. wappa-0.1.0.dist-info/WHEEL +4 -0
  210. wappa-0.1.0.dist-info/entry_points.txt +2 -0
  211. wappa-0.1.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,3 @@
1
+ """
2
+ Webhook schemas package.
3
+ """
@@ -0,0 +1,6 @@
1
+ """
2
+ WhatsApp Business API webhook schemas.
3
+
4
+ This package contains Pydantic models for processing WhatsApp Business Platform
5
+ webhooks, including all message types, status updates, and system events.
6
+ """
@@ -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
+ ]