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,523 @@
1
+ """
2
+ Messaging interface for platform-agnostic communication.
3
+
4
+ This interface defines the contract for messaging operations that can be
5
+ implemented across different messaging platforms (WhatsApp, Telegram, Teams, etc.).
6
+
7
+ Implements messaging operations:
8
+ - Basic messaging (send_text, mark_as_read)
9
+ - Media messaging (send_image, send_video, send_audio, send_document, send_sticker)
10
+ - Interactive messaging (send_button_message, send_list_message, send_cta_message)
11
+ - Template messaging (send_text_template, send_media_template, send_location_template)
12
+
13
+ Implements specialized messaging:
14
+ - Specialized messaging (send_contact, send_location, send_location_request)
15
+ """
16
+
17
+ from abc import ABC, abstractmethod
18
+ from pathlib import Path
19
+ from typing import TYPE_CHECKING
20
+
21
+ from wappa.schemas.core.types import PlatformType
22
+
23
+ if TYPE_CHECKING:
24
+ from wappa.messaging.whatsapp.models.basic_models import MessageResult
25
+
26
+
27
+ class IMessenger(ABC):
28
+ """
29
+ Messaging interface for platform-agnostic communication.
30
+
31
+ Provides messaging operations:
32
+ - Basic messaging: send_text, mark_as_read
33
+ - Media messaging: send_image, send_video, send_audio, send_document, send_sticker
34
+ - Interactive messaging: send_button_message, send_list_message, send_cta_message
35
+ - Template messaging: send_text_template, send_media_template, send_location_template
36
+
37
+ Specialized messaging:
38
+ - Specialized messaging: send_contact, send_location, send_location_request
39
+
40
+ Key Design Decisions:
41
+ - tenant_id property provides the platform-specific tenant identifier
42
+ - All methods return MessageResult for consistent response handling
43
+ - Supports typing indicator in mark_as_read as specifically requested
44
+ - Media methods support both URLs and file paths for flexibility
45
+ """
46
+
47
+ @property
48
+ @abstractmethod
49
+ def platform(self) -> PlatformType:
50
+ """Get the platform this messenger handles.
51
+
52
+ Returns:
53
+ PlatformType enum value (e.g., WHATSAPP, TELEGRAM, TEAMS)
54
+ """
55
+ pass
56
+
57
+ @property
58
+ @abstractmethod
59
+ def tenant_id(self) -> str:
60
+ """Get the tenant ID this messenger serves.
61
+
62
+ Note: In WhatsApp context, this is the phone_number_id.
63
+ Different platforms may use different tenant identifiers.
64
+
65
+ Returns:
66
+ Platform-specific tenant identifier
67
+ """
68
+ pass
69
+
70
+ @abstractmethod
71
+ async def send_text(
72
+ self,
73
+ text: str,
74
+ recipient: str,
75
+ reply_to_message_id: str | None = None,
76
+ disable_preview: bool = False,
77
+ ) -> "MessageResult":
78
+ """Send a text message.
79
+
80
+ Args:
81
+ text: Text content of the message (1-4096 characters)
82
+ recipient: Recipient identifier (phone number, user ID, etc.)
83
+ reply_to_message_id: Optional message ID to reply to (creates thread)
84
+ disable_preview: Whether to disable URL preview for links
85
+
86
+ Returns:
87
+ MessageResult with operation status and metadata
88
+
89
+ Raises:
90
+ Platform-specific exceptions for API failures
91
+ """
92
+ pass
93
+
94
+ @abstractmethod
95
+ async def mark_as_read(
96
+ self, message_id: str, typing: bool = False
97
+ ) -> "MessageResult":
98
+ """Mark a message as read, optionally with typing indicator.
99
+
100
+ Key requirement: Support for typing indicator boolean parameter.
101
+ When typing=True, shows typing indicator to the sender.
102
+
103
+ Args:
104
+ message_id: Platform-specific message identifier to mark as read
105
+ typing: Whether to show typing indicator when marking as read
106
+
107
+ Returns:
108
+ MessageResult with operation status and metadata
109
+
110
+ Raises:
111
+ Platform-specific exceptions for API failures
112
+ """
113
+ pass
114
+
115
+ # Media Messaging Methods
116
+ @abstractmethod
117
+ async def send_image(
118
+ self,
119
+ image_source: str | Path,
120
+ recipient: str,
121
+ caption: str | None = None,
122
+ reply_to_message_id: str | None = None,
123
+ ) -> "MessageResult":
124
+ """Send an image message.
125
+
126
+ Supports JPEG and PNG images up to 5MB.
127
+ Images must be 8-bit, RGB or RGBA (WhatsApp Cloud API 2025).
128
+
129
+ Args:
130
+ image_source: Image URL or file path
131
+ recipient: Recipient identifier
132
+ caption: Optional caption for the image (max 1024 characters)
133
+ reply_to_message_id: Optional message ID to reply to
134
+
135
+ Returns:
136
+ MessageResult with operation status and metadata
137
+
138
+ Raises:
139
+ Platform-specific exceptions for API failures
140
+ """
141
+ pass
142
+
143
+ @abstractmethod
144
+ async def send_video(
145
+ self,
146
+ video_source: str | Path,
147
+ recipient: str,
148
+ caption: str | None = None,
149
+ reply_to_message_id: str | None = None,
150
+ ) -> "MessageResult":
151
+ """Send a video message.
152
+
153
+ Supports MP4 and 3GP videos up to 16MB.
154
+ Only H.264 video codec and AAC audio codec supported.
155
+ Single audio stream or no audio stream only (WhatsApp Cloud API 2025).
156
+
157
+ Args:
158
+ video_source: Video URL or file path
159
+ recipient: Recipient identifier
160
+ caption: Optional caption for the video (max 1024 characters)
161
+ reply_to_message_id: Optional message ID to reply to
162
+
163
+ Returns:
164
+ MessageResult with operation status and metadata
165
+
166
+ Raises:
167
+ Platform-specific exceptions for API failures
168
+ """
169
+ pass
170
+
171
+ @abstractmethod
172
+ async def send_audio(
173
+ self,
174
+ audio_source: str | Path,
175
+ recipient: str,
176
+ reply_to_message_id: str | None = None,
177
+ ) -> "MessageResult":
178
+ """Send an audio message.
179
+
180
+ Supports AAC, AMR, MP3, M4A, and OGG audio up to 16MB.
181
+ OGG must use OPUS codecs only, mono input only (WhatsApp Cloud API 2025).
182
+
183
+ Args:
184
+ audio_source: Audio URL or file path
185
+ recipient: Recipient identifier
186
+ reply_to_message_id: Optional message ID to reply to
187
+
188
+ Returns:
189
+ MessageResult with operation status and metadata
190
+
191
+ Note:
192
+ Audio messages do not support captions.
193
+
194
+ Raises:
195
+ Platform-specific exceptions for API failures
196
+ """
197
+ pass
198
+
199
+ @abstractmethod
200
+ async def send_document(
201
+ self,
202
+ document_source: str | Path,
203
+ recipient: str,
204
+ filename: str | None = None,
205
+ caption: str | None = None,
206
+ reply_to_message_id: str | None = None,
207
+ ) -> "MessageResult":
208
+ """Send a document message.
209
+
210
+ Supports TXT, PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX up to 100MB.
211
+
212
+ Args:
213
+ document_source: Document URL or file path
214
+ recipient: Recipient identifier
215
+ filename: Optional filename for the document
216
+ caption: Optional caption for the document (max 1024 characters)
217
+ reply_to_message_id: Optional message ID to reply to
218
+
219
+ Returns:
220
+ MessageResult with operation status and metadata
221
+
222
+ Raises:
223
+ Platform-specific exceptions for API failures
224
+ """
225
+ pass
226
+
227
+ @abstractmethod
228
+ async def send_sticker(
229
+ self,
230
+ sticker_source: str | Path,
231
+ recipient: str,
232
+ reply_to_message_id: str | None = None,
233
+ ) -> "MessageResult":
234
+ """Send a sticker message.
235
+
236
+ Supports WebP images only.
237
+ Static stickers: 100KB max, Animated stickers: 500KB max.
238
+
239
+ Args:
240
+ sticker_source: Sticker URL or file path (WebP format)
241
+ recipient: Recipient identifier
242
+ reply_to_message_id: Optional message ID to reply to
243
+
244
+ Returns:
245
+ MessageResult with operation status and metadata
246
+
247
+ Note:
248
+ Sticker messages do not support captions.
249
+
250
+ Raises:
251
+ Platform-specific exceptions for API failures
252
+ """
253
+ pass
254
+
255
+ # Interactive Messaging Methods
256
+ @abstractmethod
257
+ async def send_button_message(
258
+ self,
259
+ buttons: list[dict[str, str]],
260
+ recipient: str,
261
+ body: str,
262
+ header: dict | None = None,
263
+ footer: str | None = None,
264
+ reply_to_message_id: str | None = None,
265
+ ) -> "MessageResult":
266
+ """Send an interactive button message.
267
+
268
+ Supports up to 3 quick reply buttons with optional header and footer.
269
+ Based on WhatsApp Cloud API 2025 interactive button specifications.
270
+
271
+ Args:
272
+ buttons: List of button objects with 'id' and 'title' keys (max 3 buttons)
273
+ recipient: Recipient identifier
274
+ body: Main message text (max 1024 characters)
275
+ header: Optional header content with type and content
276
+ footer: Optional footer text (max 60 characters)
277
+ reply_to_message_id: Optional message ID to reply to
278
+
279
+ Returns:
280
+ MessageResult with operation status and metadata
281
+
282
+ Raises:
283
+ Platform-specific exceptions for API failures
284
+ """
285
+ pass
286
+
287
+ @abstractmethod
288
+ async def send_list_message(
289
+ self,
290
+ sections: list[dict],
291
+ recipient: str,
292
+ body: str,
293
+ button_text: str,
294
+ header: str | None = None,
295
+ footer: str | None = None,
296
+ reply_to_message_id: str | None = None,
297
+ ) -> "MessageResult":
298
+ """Send an interactive list message.
299
+
300
+ Supports sectioned lists with rows (max 10 sections, 10 rows per section).
301
+ Based on WhatsApp Cloud API 2025 interactive list specifications.
302
+
303
+ Args:
304
+ sections: List of section objects with title and rows
305
+ recipient: Recipient identifier
306
+ body: Main message text (max 4096 characters)
307
+ button_text: Text for the button that opens the list (max 20 characters)
308
+ header: Optional header text (max 60 characters)
309
+ footer: Optional footer text (max 60 characters)
310
+ reply_to_message_id: Optional message ID to reply to
311
+
312
+ Returns:
313
+ MessageResult with operation status and metadata
314
+
315
+ Raises:
316
+ Platform-specific exceptions for API failures
317
+ """
318
+ pass
319
+
320
+ @abstractmethod
321
+ async def send_cta_message(
322
+ self,
323
+ button_text: str,
324
+ button_url: str,
325
+ recipient: str,
326
+ body: str,
327
+ header: str | None = None,
328
+ footer: str | None = None,
329
+ reply_to_message_id: str | None = None,
330
+ ) -> "MessageResult":
331
+ """Send an interactive call-to-action URL button message.
332
+
333
+ Supports external URL buttons for call-to-action scenarios.
334
+ Based on WhatsApp Cloud API 2025 CTA URL specifications.
335
+
336
+ Args:
337
+ button_text: Text to display on the button
338
+ button_url: URL to load when button is tapped (must start with http:// or https://)
339
+ recipient: Recipient identifier
340
+ body: Main message text
341
+ header: Optional header text
342
+ footer: Optional footer text
343
+ reply_to_message_id: Optional message ID to reply to
344
+
345
+ Returns:
346
+ MessageResult with operation status and metadata
347
+
348
+ Raises:
349
+ Platform-specific exceptions for API failures
350
+ """
351
+ pass
352
+
353
+ # Template Messaging Methods
354
+ @abstractmethod
355
+ async def send_text_template(
356
+ self,
357
+ template_name: str,
358
+ recipient: str,
359
+ body_parameters: list[dict] | None = None,
360
+ language_code: str = "es",
361
+ ) -> "MessageResult":
362
+ """Send a text-only template message.
363
+
364
+ Supports WhatsApp Business templates with parameter substitution.
365
+ Templates must be pre-approved by WhatsApp for use.
366
+
367
+ Args:
368
+ template_name: Name of the approved WhatsApp template
369
+ recipient: Recipient identifier
370
+ body_parameters: List of parameter objects for text replacement
371
+ language_code: BCP-47 language code for template (default: "es")
372
+
373
+ Returns:
374
+ MessageResult with operation status and metadata
375
+
376
+ Raises:
377
+ Platform-specific exceptions for API failures
378
+ """
379
+ pass
380
+
381
+ @abstractmethod
382
+ async def send_media_template(
383
+ self,
384
+ template_name: str,
385
+ recipient: str,
386
+ media_type: str,
387
+ media_id: str | None = None,
388
+ media_url: str | None = None,
389
+ body_parameters: list[dict] | None = None,
390
+ language_code: str = "es",
391
+ ) -> "MessageResult":
392
+ """Send a template message with media header.
393
+
394
+ Supports templates with image, video, or document headers.
395
+ Either media_id (uploaded media) or media_url (external media) must be provided.
396
+
397
+ Args:
398
+ template_name: Name of the approved WhatsApp template
399
+ recipient: Recipient identifier
400
+ media_type: Type of media header ("image", "video", "document")
401
+ media_id: ID of pre-uploaded media (exclusive with media_url)
402
+ media_url: URL of external media (exclusive with media_id)
403
+ body_parameters: List of parameter objects for text replacement
404
+ language_code: BCP-47 language code for template (default: "es")
405
+
406
+ Returns:
407
+ MessageResult with operation status and metadata
408
+
409
+ Raises:
410
+ Platform-specific exceptions for API failures
411
+ """
412
+ pass
413
+
414
+ @abstractmethod
415
+ async def send_location_template(
416
+ self,
417
+ template_name: str,
418
+ recipient: str,
419
+ latitude: str,
420
+ longitude: str,
421
+ name: str,
422
+ address: str,
423
+ body_parameters: list[dict] | None = None,
424
+ language_code: str = "es",
425
+ ) -> "MessageResult":
426
+ """Send a template message with location header.
427
+
428
+ Supports templates with geographic location headers showing a map preview.
429
+ Coordinates must be valid latitude (-90 to 90) and longitude (-180 to 180).
430
+
431
+ Args:
432
+ template_name: Name of the approved WhatsApp template
433
+ recipient: Recipient identifier
434
+ latitude: Location latitude as string (e.g., "37.483307")
435
+ longitude: Location longitude as string (e.g., "-122.148981")
436
+ name: Name/title of the location
437
+ address: Physical address of the location
438
+ body_parameters: List of parameter objects for text replacement
439
+ language_code: BCP-47 language code for template (default: "es")
440
+
441
+ Returns:
442
+ MessageResult with operation status and metadata
443
+
444
+ Raises:
445
+ Platform-specific exceptions for API failures
446
+ """
447
+ pass
448
+
449
+ # Specialized Messaging Methods
450
+ @abstractmethod
451
+ async def send_contact(
452
+ self, contact: dict, recipient: str, reply_to_message_id: str | None = None
453
+ ) -> "MessageResult":
454
+ """Send a contact card message.
455
+
456
+ Shares contact information including name, phone numbers, emails, and addresses.
457
+ Contact cards are automatically added to the recipient's address book.
458
+
459
+ Args:
460
+ contact: Contact information dictionary with required 'name' and 'phones' fields
461
+ recipient: Recipient identifier
462
+ reply_to_message_id: Optional message ID to reply to
463
+
464
+ Returns:
465
+ MessageResult with operation status and metadata
466
+
467
+ Raises:
468
+ Platform-specific exceptions for API failures
469
+ """
470
+ pass
471
+
472
+ @abstractmethod
473
+ async def send_location(
474
+ self,
475
+ latitude: float,
476
+ longitude: float,
477
+ recipient: str,
478
+ name: str | None = None,
479
+ address: str | None = None,
480
+ reply_to_message_id: str | None = None,
481
+ ) -> "MessageResult":
482
+ """Send a location message.
483
+
484
+ Shares geographic coordinates with optional location name and address.
485
+ Recipients see a map preview with the shared location.
486
+
487
+ Args:
488
+ latitude: Location latitude in decimal degrees (-90 to 90)
489
+ longitude: Location longitude in decimal degrees (-180 to 180)
490
+ recipient: Recipient identifier
491
+ name: Optional location name (e.g., "Coffee Shop")
492
+ address: Optional street address
493
+ reply_to_message_id: Optional message ID to reply to
494
+
495
+ Returns:
496
+ MessageResult with operation status and metadata
497
+
498
+ Raises:
499
+ Platform-specific exceptions for API failures
500
+ """
501
+ pass
502
+
503
+ @abstractmethod
504
+ async def send_location_request(
505
+ self, body: str, recipient: str, reply_to_message_id: str | None = None
506
+ ) -> "MessageResult":
507
+ """Send a location request message.
508
+
509
+ Sends an interactive message that prompts the recipient to share their location.
510
+ Recipients see a "Send Location" button that allows easy location sharing.
511
+
512
+ Args:
513
+ body: Request message text (max 1024 characters)
514
+ recipient: Recipient identifier
515
+ reply_to_message_id: Optional message ID to reply to
516
+
517
+ Returns:
518
+ MessageResult with operation status and metadata
519
+
520
+ Raises:
521
+ Platform-specific exceptions for API failures
522
+ """
523
+ pass
@@ -0,0 +1,151 @@
1
+ """
2
+ Pub/Sub repository interface.
3
+
4
+ Defines contract for Redis pub/sub messaging in Redis.
5
+ """
6
+
7
+ from abc import abstractmethod
8
+ from collections.abc import AsyncIterator, Callable
9
+ from typing import Any
10
+
11
+ from .base_repository import IBaseRepository
12
+
13
+
14
+ class IPubSubRepository(IBaseRepository):
15
+ """
16
+ Interface for Redis pub/sub messaging.
17
+
18
+ Handles real-time messaging and event broadcasting with context binding.
19
+ Uses the 'pubsub' Redis pool (database 5).
20
+ """
21
+
22
+ @abstractmethod
23
+ async def publish_message(
24
+ self, channel: str, message: dict[str, Any], user_id: str | None = None
25
+ ) -> int:
26
+ """
27
+ Publish message to channel.
28
+
29
+ Args:
30
+ channel: Channel name
31
+ message: Message data
32
+ user_id: Optional user context for filtering
33
+
34
+ Returns:
35
+ Number of subscribers that received the message
36
+ """
37
+ pass
38
+
39
+ @abstractmethod
40
+ async def subscribe_to_channel(
41
+ self, channel: str, callback: Callable[[dict[str, Any]], None]
42
+ ) -> None:
43
+ """
44
+ Subscribe to channel with callback.
45
+
46
+ Args:
47
+ channel: Channel name
48
+ callback: Function to call when message received
49
+ """
50
+ pass
51
+
52
+ @abstractmethod
53
+ async def unsubscribe_from_channel(self, channel: str) -> None:
54
+ """
55
+ Unsubscribe from channel.
56
+
57
+ Args:
58
+ channel: Channel name
59
+ """
60
+ pass
61
+
62
+ @abstractmethod
63
+ async def subscribe_to_pattern(
64
+ self, pattern: str, callback: Callable[[str, dict[str, Any]], None]
65
+ ) -> None:
66
+ """
67
+ Subscribe to channel pattern with callback.
68
+
69
+ Args:
70
+ pattern: Channel pattern (e.g., 'user:*:events')
71
+ callback: Function to call with (channel, message) when message received
72
+ """
73
+ pass
74
+
75
+ @abstractmethod
76
+ async def unsubscribe_from_pattern(self, pattern: str) -> None:
77
+ """
78
+ Unsubscribe from channel pattern.
79
+
80
+ Args:
81
+ pattern: Channel pattern
82
+ """
83
+ pass
84
+
85
+ @abstractmethod
86
+ async def get_channel_subscribers(self, channel: str) -> int:
87
+ """
88
+ Get number of subscribers for channel.
89
+
90
+ Args:
91
+ channel: Channel name
92
+
93
+ Returns:
94
+ Number of subscribers
95
+ """
96
+ pass
97
+
98
+ @abstractmethod
99
+ async def get_active_channels(self) -> list[str]:
100
+ """
101
+ Get list of active channels.
102
+
103
+ Returns:
104
+ List of channel names with active subscribers
105
+ """
106
+ pass
107
+
108
+ @abstractmethod
109
+ async def publish_user_event(
110
+ self, user_id: str, event_type: str, event_data: dict[str, Any]
111
+ ) -> int:
112
+ """
113
+ Publish user-specific event.
114
+
115
+ Args:
116
+ user_id: User identifier
117
+ event_type: Type of event
118
+ event_data: Event data
119
+
120
+ Returns:
121
+ Number of subscribers that received the event
122
+ """
123
+ pass
124
+
125
+ @abstractmethod
126
+ async def subscribe_to_user_events(
127
+ self, user_id: str, callback: Callable[[str, dict[str, Any]], None]
128
+ ) -> None:
129
+ """
130
+ Subscribe to all events for specific user.
131
+
132
+ Args:
133
+ user_id: User identifier
134
+ callback: Function to call with (event_type, event_data)
135
+ """
136
+ pass
137
+
138
+ @abstractmethod
139
+ async def listen_for_messages(
140
+ self, channels: list[str]
141
+ ) -> AsyncIterator[dict[str, Any]]:
142
+ """
143
+ Listen for messages on multiple channels.
144
+
145
+ Args:
146
+ channels: List of channel names
147
+
148
+ Yields:
149
+ Message data as it arrives
150
+ """
151
+ pass