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,450 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Media factory pattern for creating platform-specific media message objects.
|
|
3
|
+
|
|
4
|
+
This factory creates properly formatted media message objects based on
|
|
5
|
+
existing payload generation logic from whatsapp_latest/services/handle_media.py.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from wappa.schemas.core.types import PlatformType
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class MediaFactory(ABC):
|
|
15
|
+
"""
|
|
16
|
+
Abstract factory for creating platform-specific media message objects.
|
|
17
|
+
|
|
18
|
+
This factory creates properly formatted media message objects that can be
|
|
19
|
+
sent through the IMessenger interface while maintaining platform
|
|
20
|
+
compatibility and type safety.
|
|
21
|
+
|
|
22
|
+
Based on existing payload generation patterns from handle_media.py.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def platform(self) -> PlatformType:
|
|
28
|
+
"""Get the platform this factory creates messages for."""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
# Media Message Creation Methods
|
|
32
|
+
@abstractmethod
|
|
33
|
+
def create_image_message(
|
|
34
|
+
self,
|
|
35
|
+
media_reference: str,
|
|
36
|
+
recipient: str,
|
|
37
|
+
caption: str | None = None,
|
|
38
|
+
reply_to_message_id: str | None = None,
|
|
39
|
+
is_url: bool = False,
|
|
40
|
+
) -> dict[str, Any]:
|
|
41
|
+
"""Create an image message payload.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
media_reference: Media ID or URL
|
|
45
|
+
recipient: Recipient identifier
|
|
46
|
+
caption: Optional caption for the image
|
|
47
|
+
reply_to_message_id: Optional message ID to reply to
|
|
48
|
+
is_url: Whether media_reference is a URL (True) or media ID (False)
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Platform-specific image message payload
|
|
52
|
+
"""
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
@abstractmethod
|
|
56
|
+
def create_video_message(
|
|
57
|
+
self,
|
|
58
|
+
media_reference: str,
|
|
59
|
+
recipient: str,
|
|
60
|
+
caption: str | None = None,
|
|
61
|
+
reply_to_message_id: str | None = None,
|
|
62
|
+
is_url: bool = False,
|
|
63
|
+
) -> dict[str, Any]:
|
|
64
|
+
"""Create a video message payload.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
media_reference: Media ID or URL
|
|
68
|
+
recipient: Recipient identifier
|
|
69
|
+
caption: Optional caption for the video
|
|
70
|
+
reply_to_message_id: Optional message ID to reply to
|
|
71
|
+
is_url: Whether media_reference is a URL (True) or media ID (False)
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Platform-specific video message payload
|
|
75
|
+
"""
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
@abstractmethod
|
|
79
|
+
def create_audio_message(
|
|
80
|
+
self,
|
|
81
|
+
media_reference: str,
|
|
82
|
+
recipient: str,
|
|
83
|
+
reply_to_message_id: str | None = None,
|
|
84
|
+
is_url: bool = False,
|
|
85
|
+
) -> dict[str, Any]:
|
|
86
|
+
"""Create an audio message payload.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
media_reference: Media ID or URL
|
|
90
|
+
recipient: Recipient identifier
|
|
91
|
+
reply_to_message_id: Optional message ID to reply to
|
|
92
|
+
is_url: Whether media_reference is a URL (True) or media ID (False)
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
Platform-specific audio message payload
|
|
96
|
+
"""
|
|
97
|
+
pass
|
|
98
|
+
|
|
99
|
+
@abstractmethod
|
|
100
|
+
def create_document_message(
|
|
101
|
+
self,
|
|
102
|
+
media_reference: str,
|
|
103
|
+
recipient: str,
|
|
104
|
+
filename: str | None = None,
|
|
105
|
+
reply_to_message_id: str | None = None,
|
|
106
|
+
is_url: bool = False,
|
|
107
|
+
) -> dict[str, Any]:
|
|
108
|
+
"""Create a document message payload.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
media_reference: Media ID or URL
|
|
112
|
+
recipient: Recipient identifier
|
|
113
|
+
filename: Optional filename for the document
|
|
114
|
+
reply_to_message_id: Optional message ID to reply to
|
|
115
|
+
is_url: Whether media_reference is a URL (True) or media ID (False)
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Platform-specific document message payload
|
|
119
|
+
"""
|
|
120
|
+
pass
|
|
121
|
+
|
|
122
|
+
@abstractmethod
|
|
123
|
+
def create_sticker_message(
|
|
124
|
+
self,
|
|
125
|
+
media_reference: str,
|
|
126
|
+
recipient: str,
|
|
127
|
+
reply_to_message_id: str | None = None,
|
|
128
|
+
is_url: bool = False,
|
|
129
|
+
) -> dict[str, Any]:
|
|
130
|
+
"""Create a sticker message payload.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
media_reference: Media ID or URL
|
|
134
|
+
recipient: Recipient identifier
|
|
135
|
+
reply_to_message_id: Optional message ID to reply to
|
|
136
|
+
is_url: Whether media_reference is a URL (True) or media ID (False)
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
Platform-specific sticker message payload
|
|
140
|
+
"""
|
|
141
|
+
pass
|
|
142
|
+
|
|
143
|
+
# Validation Methods
|
|
144
|
+
@abstractmethod
|
|
145
|
+
def validate_media_message(self, message_payload: dict[str, Any]) -> bool:
|
|
146
|
+
"""Validate media message payload against platform constraints.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
message_payload: Media message payload to validate
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
True if payload is valid, False otherwise
|
|
153
|
+
"""
|
|
154
|
+
pass
|
|
155
|
+
|
|
156
|
+
@abstractmethod
|
|
157
|
+
def get_media_limits(self) -> dict[str, Any]:
|
|
158
|
+
"""Get platform-specific media limits.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
Dictionary containing platform-specific media limits
|
|
162
|
+
"""
|
|
163
|
+
pass
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class WhatsAppMediaFactory(MediaFactory):
|
|
167
|
+
"""WhatsApp implementation of the media factory.
|
|
168
|
+
|
|
169
|
+
Based on existing payload generation patterns from
|
|
170
|
+
whatsapp_latest/services/handle_media.py send_media() method.
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def platform(self) -> PlatformType:
|
|
175
|
+
"""Get the platform this factory creates messages for."""
|
|
176
|
+
return PlatformType.WHATSAPP
|
|
177
|
+
|
|
178
|
+
def create_image_message(
|
|
179
|
+
self,
|
|
180
|
+
media_reference: str,
|
|
181
|
+
recipient: str,
|
|
182
|
+
caption: str | None = None,
|
|
183
|
+
reply_to_message_id: str | None = None,
|
|
184
|
+
is_url: bool = False,
|
|
185
|
+
) -> dict[str, Any]:
|
|
186
|
+
"""Create WhatsApp image message payload.
|
|
187
|
+
|
|
188
|
+
Based on existing send_media() logic for MediaType.IMAGE.
|
|
189
|
+
"""
|
|
190
|
+
payload = {
|
|
191
|
+
"messaging_product": "whatsapp",
|
|
192
|
+
"recipient_type": "individual",
|
|
193
|
+
"to": recipient,
|
|
194
|
+
"type": "image",
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
# Create media object based on reference type
|
|
198
|
+
if is_url:
|
|
199
|
+
media_obj = {"link": media_reference}
|
|
200
|
+
else:
|
|
201
|
+
media_obj = {"id": media_reference}
|
|
202
|
+
|
|
203
|
+
# Add optional caption
|
|
204
|
+
if caption:
|
|
205
|
+
media_obj["caption"] = caption
|
|
206
|
+
|
|
207
|
+
payload["image"] = media_obj
|
|
208
|
+
|
|
209
|
+
# Add reply context if specified
|
|
210
|
+
if reply_to_message_id:
|
|
211
|
+
payload["context"] = {"message_id": reply_to_message_id}
|
|
212
|
+
|
|
213
|
+
return payload
|
|
214
|
+
|
|
215
|
+
def create_video_message(
|
|
216
|
+
self,
|
|
217
|
+
media_reference: str,
|
|
218
|
+
recipient: str,
|
|
219
|
+
caption: str | None = None,
|
|
220
|
+
reply_to_message_id: str | None = None,
|
|
221
|
+
is_url: bool = False,
|
|
222
|
+
) -> dict[str, Any]:
|
|
223
|
+
"""Create WhatsApp video message payload.
|
|
224
|
+
|
|
225
|
+
Based on existing send_media() logic for MediaType.VIDEO.
|
|
226
|
+
"""
|
|
227
|
+
payload = {
|
|
228
|
+
"messaging_product": "whatsapp",
|
|
229
|
+
"recipient_type": "individual",
|
|
230
|
+
"to": recipient,
|
|
231
|
+
"type": "video",
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
# Create media object based on reference type
|
|
235
|
+
if is_url:
|
|
236
|
+
media_obj = {"link": media_reference}
|
|
237
|
+
else:
|
|
238
|
+
media_obj = {"id": media_reference}
|
|
239
|
+
|
|
240
|
+
# Add optional caption
|
|
241
|
+
if caption:
|
|
242
|
+
media_obj["caption"] = caption
|
|
243
|
+
|
|
244
|
+
payload["video"] = media_obj
|
|
245
|
+
|
|
246
|
+
# Add reply context if specified
|
|
247
|
+
if reply_to_message_id:
|
|
248
|
+
payload["context"] = {"message_id": reply_to_message_id}
|
|
249
|
+
|
|
250
|
+
return payload
|
|
251
|
+
|
|
252
|
+
def create_audio_message(
|
|
253
|
+
self,
|
|
254
|
+
media_reference: str,
|
|
255
|
+
recipient: str,
|
|
256
|
+
reply_to_message_id: str | None = None,
|
|
257
|
+
is_url: bool = False,
|
|
258
|
+
) -> dict[str, Any]:
|
|
259
|
+
"""Create WhatsApp audio message payload.
|
|
260
|
+
|
|
261
|
+
Based on existing send_media() logic for MediaType.AUDIO.
|
|
262
|
+
Note: Audio messages do not support captions.
|
|
263
|
+
"""
|
|
264
|
+
payload = {
|
|
265
|
+
"messaging_product": "whatsapp",
|
|
266
|
+
"recipient_type": "individual",
|
|
267
|
+
"to": recipient,
|
|
268
|
+
"type": "audio",
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
# Create media object based on reference type
|
|
272
|
+
if is_url:
|
|
273
|
+
media_obj = {"link": media_reference}
|
|
274
|
+
else:
|
|
275
|
+
media_obj = {"id": media_reference}
|
|
276
|
+
|
|
277
|
+
payload["audio"] = media_obj
|
|
278
|
+
|
|
279
|
+
# Add reply context if specified
|
|
280
|
+
if reply_to_message_id:
|
|
281
|
+
payload["context"] = {"message_id": reply_to_message_id}
|
|
282
|
+
|
|
283
|
+
return payload
|
|
284
|
+
|
|
285
|
+
def create_document_message(
|
|
286
|
+
self,
|
|
287
|
+
media_reference: str,
|
|
288
|
+
recipient: str,
|
|
289
|
+
filename: str | None = None,
|
|
290
|
+
reply_to_message_id: str | None = None,
|
|
291
|
+
is_url: bool = False,
|
|
292
|
+
) -> dict[str, Any]:
|
|
293
|
+
"""Create WhatsApp document message payload.
|
|
294
|
+
|
|
295
|
+
Based on existing send_media() logic for MediaType.DOCUMENT.
|
|
296
|
+
"""
|
|
297
|
+
payload = {
|
|
298
|
+
"messaging_product": "whatsapp",
|
|
299
|
+
"recipient_type": "individual",
|
|
300
|
+
"to": recipient,
|
|
301
|
+
"type": "document",
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
# Create media object based on reference type
|
|
305
|
+
if is_url:
|
|
306
|
+
media_obj = {"link": media_reference}
|
|
307
|
+
else:
|
|
308
|
+
media_obj = {"id": media_reference}
|
|
309
|
+
|
|
310
|
+
# Add optional filename
|
|
311
|
+
if filename:
|
|
312
|
+
media_obj["filename"] = filename
|
|
313
|
+
|
|
314
|
+
payload["document"] = media_obj
|
|
315
|
+
|
|
316
|
+
# Add reply context if specified
|
|
317
|
+
if reply_to_message_id:
|
|
318
|
+
payload["context"] = {"message_id": reply_to_message_id}
|
|
319
|
+
|
|
320
|
+
return payload
|
|
321
|
+
|
|
322
|
+
def create_sticker_message(
|
|
323
|
+
self,
|
|
324
|
+
media_reference: str,
|
|
325
|
+
recipient: str,
|
|
326
|
+
reply_to_message_id: str | None = None,
|
|
327
|
+
is_url: bool = False,
|
|
328
|
+
) -> dict[str, Any]:
|
|
329
|
+
"""Create WhatsApp sticker message payload.
|
|
330
|
+
|
|
331
|
+
Based on existing send_media() logic for MediaType.STICKER.
|
|
332
|
+
Note: Sticker messages do not support captions.
|
|
333
|
+
"""
|
|
334
|
+
payload = {
|
|
335
|
+
"messaging_product": "whatsapp",
|
|
336
|
+
"recipient_type": "individual",
|
|
337
|
+
"to": recipient,
|
|
338
|
+
"type": "sticker",
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
# Create media object based on reference type
|
|
342
|
+
if is_url:
|
|
343
|
+
media_obj = {"link": media_reference}
|
|
344
|
+
else:
|
|
345
|
+
media_obj = {"id": media_reference}
|
|
346
|
+
|
|
347
|
+
payload["sticker"] = media_obj
|
|
348
|
+
|
|
349
|
+
# Add reply context if specified
|
|
350
|
+
if reply_to_message_id:
|
|
351
|
+
payload["context"] = {"message_id": reply_to_message_id}
|
|
352
|
+
|
|
353
|
+
return payload
|
|
354
|
+
|
|
355
|
+
def validate_media_message(self, message_payload: dict[str, Any]) -> bool:
|
|
356
|
+
"""Validate WhatsApp media message payload.
|
|
357
|
+
|
|
358
|
+
Performs validation against WhatsApp Business API requirements.
|
|
359
|
+
"""
|
|
360
|
+
try:
|
|
361
|
+
# Check required fields
|
|
362
|
+
if "messaging_product" not in message_payload:
|
|
363
|
+
return False
|
|
364
|
+
if message_payload["messaging_product"] != "whatsapp":
|
|
365
|
+
return False
|
|
366
|
+
if "to" not in message_payload:
|
|
367
|
+
return False
|
|
368
|
+
if "type" not in message_payload:
|
|
369
|
+
return False
|
|
370
|
+
|
|
371
|
+
message_type = message_payload["type"]
|
|
372
|
+
valid_types = ["image", "video", "audio", "document", "sticker"]
|
|
373
|
+
if message_type not in valid_types:
|
|
374
|
+
return False
|
|
375
|
+
|
|
376
|
+
# Check that media object exists
|
|
377
|
+
if message_type not in message_payload:
|
|
378
|
+
return False
|
|
379
|
+
|
|
380
|
+
media_obj = message_payload[message_type]
|
|
381
|
+
|
|
382
|
+
# Must have either 'id' or 'link'
|
|
383
|
+
if "id" not in media_obj and "link" not in media_obj:
|
|
384
|
+
return False
|
|
385
|
+
|
|
386
|
+
# Validate caption length if present
|
|
387
|
+
if "caption" in media_obj:
|
|
388
|
+
if len(media_obj["caption"]) > 1024:
|
|
389
|
+
return False
|
|
390
|
+
# Caption not allowed for audio and sticker
|
|
391
|
+
if message_type in ["audio", "sticker"]:
|
|
392
|
+
return False
|
|
393
|
+
|
|
394
|
+
return True
|
|
395
|
+
|
|
396
|
+
except (KeyError, TypeError):
|
|
397
|
+
return False
|
|
398
|
+
|
|
399
|
+
def get_media_limits(self) -> dict[str, Any]:
|
|
400
|
+
"""Get WhatsApp-specific media limits.
|
|
401
|
+
|
|
402
|
+
Based on existing MediaType.get_max_file_size() and supported types.
|
|
403
|
+
"""
|
|
404
|
+
return {
|
|
405
|
+
"max_caption_length": 1024,
|
|
406
|
+
"max_filename_length": 255,
|
|
407
|
+
"supported_media_types": {
|
|
408
|
+
"image": ["image/jpeg", "image/png"],
|
|
409
|
+
"video": ["video/mp4", "video/3gpp"],
|
|
410
|
+
"audio": [
|
|
411
|
+
"audio/aac",
|
|
412
|
+
"audio/amr",
|
|
413
|
+
"audio/mpeg",
|
|
414
|
+
"audio/mp4",
|
|
415
|
+
"audio/ogg",
|
|
416
|
+
],
|
|
417
|
+
"document": [
|
|
418
|
+
"text/plain",
|
|
419
|
+
"application/pdf",
|
|
420
|
+
"application/vnd.ms-powerpoint",
|
|
421
|
+
"application/msword",
|
|
422
|
+
"application/vnd.ms-excel",
|
|
423
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
424
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
425
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
426
|
+
],
|
|
427
|
+
"sticker": ["image/webp"],
|
|
428
|
+
},
|
|
429
|
+
"max_file_sizes": {
|
|
430
|
+
"image": 5 * 1024 * 1024, # 5MB
|
|
431
|
+
"video": 16 * 1024 * 1024, # 16MB
|
|
432
|
+
"audio": 16 * 1024 * 1024, # 16MB
|
|
433
|
+
"document": 100 * 1024 * 1024, # 100MB
|
|
434
|
+
"sticker": 500 * 1024, # 500KB (animated), 100KB (static)
|
|
435
|
+
},
|
|
436
|
+
"caption_support": {
|
|
437
|
+
"image": True,
|
|
438
|
+
"video": True,
|
|
439
|
+
"audio": False,
|
|
440
|
+
"document": False, # Uses filename instead
|
|
441
|
+
"sticker": False,
|
|
442
|
+
},
|
|
443
|
+
"filename_support": {
|
|
444
|
+
"document": True,
|
|
445
|
+
"image": False,
|
|
446
|
+
"video": False,
|
|
447
|
+
"audio": False,
|
|
448
|
+
"sticker": False,
|
|
449
|
+
},
|
|
450
|
+
}
|