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,550 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Webhook processor factory for platform-agnostic processor creation.
|
|
3
|
+
|
|
4
|
+
This module provides factory classes for creating platform-specific processors,
|
|
5
|
+
schemas, and webhook containers based on incoming webhook data.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Optional
|
|
9
|
+
|
|
10
|
+
from wappa.core.logging.logger import get_logger
|
|
11
|
+
from wappa.processors.base_processor import BaseWebhookProcessor, ProcessorError
|
|
12
|
+
from wappa.schemas.core.base_webhook import BaseWebhook
|
|
13
|
+
from wappa.schemas.core.types import ErrorCode, PlatformType
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class PlatformDetector:
|
|
17
|
+
"""
|
|
18
|
+
Platform detection utility for identifying messaging platforms from webhook data.
|
|
19
|
+
|
|
20
|
+
Uses various heuristics including URL patterns, headers, payload structure,
|
|
21
|
+
and platform-specific identifiers to determine the source platform.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def detect_from_payload(payload: dict[str, Any]) -> PlatformType | None:
|
|
26
|
+
"""
|
|
27
|
+
Detect platform from webhook payload structure.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
payload: Raw webhook payload dictionary
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Detected platform type or None if cannot determine
|
|
34
|
+
"""
|
|
35
|
+
if not isinstance(payload, dict):
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
# WhatsApp Business API detection
|
|
39
|
+
if payload.get("object") == "whatsapp_business_account" and "entry" in payload:
|
|
40
|
+
return PlatformType.WHATSAPP
|
|
41
|
+
|
|
42
|
+
# Telegram Bot API detection
|
|
43
|
+
if "update_id" in payload and (
|
|
44
|
+
"message" in payload or "callback_query" in payload
|
|
45
|
+
):
|
|
46
|
+
return PlatformType.TELEGRAM
|
|
47
|
+
|
|
48
|
+
# Microsoft Teams detection
|
|
49
|
+
if (
|
|
50
|
+
payload.get("type") in ["message", "messageReaction"]
|
|
51
|
+
and "channelData" in payload
|
|
52
|
+
and payload.get("channelData", {}).get("tenant")
|
|
53
|
+
):
|
|
54
|
+
return PlatformType.TEAMS
|
|
55
|
+
|
|
56
|
+
# Instagram Messaging API detection (similar to WhatsApp but different object)
|
|
57
|
+
if payload.get("object") == "instagram" and "entry" in payload:
|
|
58
|
+
return PlatformType.INSTAGRAM
|
|
59
|
+
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
def detect_from_url(url_path: str) -> PlatformType | None:
|
|
64
|
+
"""
|
|
65
|
+
Detect platform from webhook URL path.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
url_path: The URL path of the webhook endpoint
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Detected platform type or None if cannot determine
|
|
72
|
+
"""
|
|
73
|
+
path_lower = url_path.lower()
|
|
74
|
+
|
|
75
|
+
if "/whatsapp" in path_lower:
|
|
76
|
+
return PlatformType.WHATSAPP
|
|
77
|
+
elif "/telegram" in path_lower:
|
|
78
|
+
return PlatformType.TELEGRAM
|
|
79
|
+
elif "/teams" in path_lower:
|
|
80
|
+
return PlatformType.TEAMS
|
|
81
|
+
elif "/instagram" in path_lower:
|
|
82
|
+
return PlatformType.INSTAGRAM
|
|
83
|
+
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
@staticmethod
|
|
87
|
+
def detect_from_headers(headers: dict[str, str]) -> PlatformType | None:
|
|
88
|
+
"""
|
|
89
|
+
Detect platform from HTTP headers.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
headers: Dictionary of HTTP headers
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
Detected platform type or None if cannot determine
|
|
96
|
+
"""
|
|
97
|
+
headers_lower = {k.lower(): v.lower() for k, v in headers.items()}
|
|
98
|
+
|
|
99
|
+
# WhatsApp/Facebook webhook signature
|
|
100
|
+
if "x-hub-signature-256" in headers_lower:
|
|
101
|
+
return PlatformType.WHATSAPP
|
|
102
|
+
|
|
103
|
+
# Telegram webhook
|
|
104
|
+
if "x-telegram-bot-api-secret-token" in headers_lower:
|
|
105
|
+
return PlatformType.TELEGRAM
|
|
106
|
+
|
|
107
|
+
# Microsoft Teams
|
|
108
|
+
if (
|
|
109
|
+
"authorization" in headers_lower
|
|
110
|
+
and "bearer" in headers_lower["authorization"]
|
|
111
|
+
):
|
|
112
|
+
# Could be Teams, but need more specific detection
|
|
113
|
+
return None
|
|
114
|
+
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
@classmethod
|
|
118
|
+
def detect_platform(
|
|
119
|
+
self,
|
|
120
|
+
payload: dict[str, Any],
|
|
121
|
+
url_path: str | None = None,
|
|
122
|
+
headers: dict[str, str] | None = None,
|
|
123
|
+
) -> PlatformType:
|
|
124
|
+
"""
|
|
125
|
+
Comprehensive platform detection using multiple heuristics.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
payload: Raw webhook payload dictionary
|
|
129
|
+
url_path: Optional URL path for additional detection
|
|
130
|
+
headers: Optional HTTP headers for additional detection
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Detected platform type
|
|
134
|
+
|
|
135
|
+
Raises:
|
|
136
|
+
ProcessorError: If platform cannot be detected
|
|
137
|
+
"""
|
|
138
|
+
# Try payload detection first (most reliable)
|
|
139
|
+
platform = self.detect_from_payload(payload)
|
|
140
|
+
if platform:
|
|
141
|
+
return platform
|
|
142
|
+
|
|
143
|
+
# Try URL path detection
|
|
144
|
+
if url_path:
|
|
145
|
+
platform = self.detect_from_url(url_path)
|
|
146
|
+
if platform:
|
|
147
|
+
return platform
|
|
148
|
+
|
|
149
|
+
# Try header detection
|
|
150
|
+
if headers:
|
|
151
|
+
platform = self.detect_from_headers(headers)
|
|
152
|
+
if platform:
|
|
153
|
+
return platform
|
|
154
|
+
|
|
155
|
+
# If all detection methods fail, raise error
|
|
156
|
+
raise ProcessorError(
|
|
157
|
+
"Unable to detect platform from webhook data",
|
|
158
|
+
ErrorCode.PLATFORM_ERROR,
|
|
159
|
+
PlatformType.WHATSAPP, # Default for error reporting
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class ProcessorFactory:
|
|
164
|
+
"""
|
|
165
|
+
Factory for creating platform-specific webhook processors.
|
|
166
|
+
|
|
167
|
+
Implements singleton pattern with processor caching for performance
|
|
168
|
+
and provides automatic platform detection and processor instantiation.
|
|
169
|
+
"""
|
|
170
|
+
|
|
171
|
+
_instance: Optional["ProcessorFactory"] = None
|
|
172
|
+
_processors: dict[PlatformType, BaseWebhookProcessor] = {}
|
|
173
|
+
|
|
174
|
+
def __new__(cls) -> "ProcessorFactory":
|
|
175
|
+
"""Singleton pattern implementation."""
|
|
176
|
+
if cls._instance is None:
|
|
177
|
+
cls._instance = super().__new__(cls)
|
|
178
|
+
return cls._instance
|
|
179
|
+
|
|
180
|
+
def __init__(self):
|
|
181
|
+
"""Initialize the factory with logger."""
|
|
182
|
+
if not hasattr(self, "_initialized"):
|
|
183
|
+
self.logger = get_logger(__name__)
|
|
184
|
+
self._initialized = True
|
|
185
|
+
|
|
186
|
+
def get_processor(self, platform: PlatformType, **kwargs) -> BaseWebhookProcessor:
|
|
187
|
+
"""
|
|
188
|
+
Get or create a processor for the specified platform.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
platform: The platform type to get processor for
|
|
192
|
+
**kwargs: Additional processor configuration parameters
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
Platform-specific processor instance
|
|
196
|
+
|
|
197
|
+
Raises:
|
|
198
|
+
ProcessorError: If processor cannot be created for platform
|
|
199
|
+
"""
|
|
200
|
+
# Return cached processor if available
|
|
201
|
+
if platform in self._processors:
|
|
202
|
+
return self._processors[platform]
|
|
203
|
+
|
|
204
|
+
# Create new processor based on platform
|
|
205
|
+
processor = self._create_processor(platform, **kwargs)
|
|
206
|
+
|
|
207
|
+
# Cache the processor
|
|
208
|
+
self._processors[platform] = processor
|
|
209
|
+
|
|
210
|
+
# Use context-aware logger that automatically gets tenant/user context
|
|
211
|
+
logger = get_logger(__name__)
|
|
212
|
+
logger.info(f"Created and cached processor for platform: {platform.value}")
|
|
213
|
+
return processor
|
|
214
|
+
|
|
215
|
+
def _create_processor(
|
|
216
|
+
self, platform: PlatformType, **kwargs
|
|
217
|
+
) -> BaseWebhookProcessor:
|
|
218
|
+
"""
|
|
219
|
+
Create a new processor instance for the specified platform.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
platform: The platform type to create processor for
|
|
223
|
+
**kwargs: Additional processor configuration parameters
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
New processor instance
|
|
227
|
+
|
|
228
|
+
Raises:
|
|
229
|
+
ProcessorError: If processor class not found for platform
|
|
230
|
+
"""
|
|
231
|
+
if platform == PlatformType.WHATSAPP:
|
|
232
|
+
from .whatsapp_processor import WhatsAppWebhookProcessor
|
|
233
|
+
|
|
234
|
+
return WhatsAppWebhookProcessor(**kwargs)
|
|
235
|
+
|
|
236
|
+
elif platform == PlatformType.TELEGRAM:
|
|
237
|
+
# TODO: Implement Telegram processor
|
|
238
|
+
# from .telegram_processor import TelegramWebhookProcessor
|
|
239
|
+
# return TelegramWebhookProcessor(**kwargs)
|
|
240
|
+
raise ProcessorError(
|
|
241
|
+
"Telegram processor not yet implemented",
|
|
242
|
+
ErrorCode.PLATFORM_ERROR,
|
|
243
|
+
platform,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
elif platform == PlatformType.TEAMS:
|
|
247
|
+
# TODO: Implement Teams processor
|
|
248
|
+
# from .teams_processor import TeamsWebhookProcessor
|
|
249
|
+
# return TeamsWebhookProcessor(**kwargs)
|
|
250
|
+
raise ProcessorError(
|
|
251
|
+
"Teams processor not yet implemented",
|
|
252
|
+
ErrorCode.PLATFORM_ERROR,
|
|
253
|
+
platform,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
elif platform == PlatformType.INSTAGRAM:
|
|
257
|
+
# TODO: Implement Instagram processor
|
|
258
|
+
# from .instagram_processor import InstagramWebhookProcessor
|
|
259
|
+
# return InstagramWebhookProcessor(**kwargs)
|
|
260
|
+
raise ProcessorError(
|
|
261
|
+
"Instagram processor not yet implemented",
|
|
262
|
+
ErrorCode.PLATFORM_ERROR,
|
|
263
|
+
platform,
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
else:
|
|
267
|
+
raise ProcessorError(
|
|
268
|
+
f"Unknown platform: {platform}", ErrorCode.PLATFORM_ERROR, platform
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
def get_processor_for_payload(
|
|
272
|
+
self,
|
|
273
|
+
payload: dict[str, Any],
|
|
274
|
+
url_path: str | None = None,
|
|
275
|
+
headers: dict[str, str] | None = None,
|
|
276
|
+
**kwargs,
|
|
277
|
+
) -> BaseWebhookProcessor:
|
|
278
|
+
"""
|
|
279
|
+
Auto-detect platform and return appropriate processor.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
payload: Raw webhook payload dictionary
|
|
283
|
+
url_path: Optional URL path for platform detection
|
|
284
|
+
headers: Optional HTTP headers for platform detection
|
|
285
|
+
**kwargs: Additional processor configuration parameters
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
Platform-specific processor instance
|
|
289
|
+
|
|
290
|
+
Raises:
|
|
291
|
+
ProcessorError: If platform cannot be detected or processor cannot be created
|
|
292
|
+
"""
|
|
293
|
+
try:
|
|
294
|
+
# Detect platform from webhook data
|
|
295
|
+
platform = PlatformDetector.detect_platform(payload, url_path, headers)
|
|
296
|
+
|
|
297
|
+
self.logger.info(f"Auto-detected platform: {platform.value}")
|
|
298
|
+
|
|
299
|
+
# Get appropriate processor
|
|
300
|
+
return self.get_processor(platform, **kwargs)
|
|
301
|
+
|
|
302
|
+
except Exception as e:
|
|
303
|
+
self.logger.error(
|
|
304
|
+
f"Failed to get processor for payload: {e}", exc_info=True
|
|
305
|
+
)
|
|
306
|
+
raise ProcessorError(
|
|
307
|
+
f"Failed to create processor: {e}",
|
|
308
|
+
ErrorCode.PROCESSING_ERROR,
|
|
309
|
+
PlatformType.WHATSAPP, # Default for error reporting
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
def get_supported_platforms(self) -> set[PlatformType]:
|
|
313
|
+
"""
|
|
314
|
+
Get the set of platforms supported by this factory.
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
Set of supported platform types
|
|
318
|
+
"""
|
|
319
|
+
return {
|
|
320
|
+
PlatformType.WHATSAPP,
|
|
321
|
+
# PlatformType.TELEGRAM, # TODO: Implement
|
|
322
|
+
# PlatformType.TEAMS, # TODO: Implement
|
|
323
|
+
# PlatformType.INSTAGRAM, # TODO: Implement
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
def is_platform_supported(self, platform: PlatformType) -> bool:
|
|
327
|
+
"""
|
|
328
|
+
Check if a platform is supported by this factory.
|
|
329
|
+
|
|
330
|
+
Args:
|
|
331
|
+
platform: Platform type to check
|
|
332
|
+
|
|
333
|
+
Returns:
|
|
334
|
+
True if platform is supported, False otherwise
|
|
335
|
+
"""
|
|
336
|
+
return platform in self.get_supported_platforms()
|
|
337
|
+
|
|
338
|
+
def get_processor_capabilities(
|
|
339
|
+
self, platform: PlatformType
|
|
340
|
+
) -> dict[str, Any] | None:
|
|
341
|
+
"""
|
|
342
|
+
Get capabilities for a specific platform processor.
|
|
343
|
+
|
|
344
|
+
Args:
|
|
345
|
+
platform: Platform type to get capabilities for
|
|
346
|
+
|
|
347
|
+
Returns:
|
|
348
|
+
Capabilities dictionary or None if platform not supported
|
|
349
|
+
"""
|
|
350
|
+
if not self.is_platform_supported(platform):
|
|
351
|
+
return None
|
|
352
|
+
|
|
353
|
+
try:
|
|
354
|
+
processor = self.get_processor(platform)
|
|
355
|
+
return processor.capabilities.to_dict()
|
|
356
|
+
except Exception as e:
|
|
357
|
+
self.logger.error(f"Failed to get capabilities for {platform.value}: {e}")
|
|
358
|
+
return None
|
|
359
|
+
|
|
360
|
+
def clear_cache(self) -> None:
|
|
361
|
+
"""Clear the processor cache, forcing recreation on next access."""
|
|
362
|
+
self._processors.clear()
|
|
363
|
+
self.logger.info("Processor cache cleared")
|
|
364
|
+
|
|
365
|
+
def get_cache_stats(self) -> dict[str, Any]:
|
|
366
|
+
"""
|
|
367
|
+
Get processor cache statistics for monitoring.
|
|
368
|
+
|
|
369
|
+
Returns:
|
|
370
|
+
Dictionary with cache statistics
|
|
371
|
+
"""
|
|
372
|
+
return {
|
|
373
|
+
"cached_processors": len(self._processors),
|
|
374
|
+
"cached_platforms": [p.value for p in self._processors.keys()],
|
|
375
|
+
"supported_platforms": [p.value for p in self.get_supported_platforms()],
|
|
376
|
+
"cache_size_bytes": sum(
|
|
377
|
+
len(str(processor)) for processor in self._processors.values()
|
|
378
|
+
),
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
class WebhookFactory:
|
|
383
|
+
"""
|
|
384
|
+
Factory for creating platform-specific webhook instances from raw payload data.
|
|
385
|
+
|
|
386
|
+
Combines platform detection with schema factory to provide a unified interface
|
|
387
|
+
for parsing webhook payloads from any supported platform.
|
|
388
|
+
"""
|
|
389
|
+
|
|
390
|
+
def __init__(self):
|
|
391
|
+
"""Initialize the webhook factory with schema factory."""
|
|
392
|
+
self.logger = get_logger(__name__)
|
|
393
|
+
|
|
394
|
+
# Import schema factory (avoid circular imports)
|
|
395
|
+
from wappa.schemas.factory import schema_factory
|
|
396
|
+
|
|
397
|
+
self.schema_factory = schema_factory
|
|
398
|
+
|
|
399
|
+
def create_webhook_from_payload(
|
|
400
|
+
self,
|
|
401
|
+
payload: dict[str, Any],
|
|
402
|
+
url_path: str | None = None,
|
|
403
|
+
headers: dict[str, str] | None = None,
|
|
404
|
+
**kwargs,
|
|
405
|
+
) -> BaseWebhook:
|
|
406
|
+
"""
|
|
407
|
+
Create a webhook instance from raw payload with automatic platform detection.
|
|
408
|
+
|
|
409
|
+
Args:
|
|
410
|
+
payload: Raw webhook payload dictionary
|
|
411
|
+
url_path: Optional URL path for platform detection
|
|
412
|
+
headers: Optional HTTP headers for platform detection
|
|
413
|
+
**kwargs: Additional parameters for webhook creation
|
|
414
|
+
|
|
415
|
+
Returns:
|
|
416
|
+
Parsed webhook instance with universal interface
|
|
417
|
+
|
|
418
|
+
Raises:
|
|
419
|
+
ProcessorError: If platform cannot be detected
|
|
420
|
+
ValidationError: If webhook data is invalid
|
|
421
|
+
"""
|
|
422
|
+
try:
|
|
423
|
+
# Detect platform from payload
|
|
424
|
+
platform = PlatformDetector.detect_platform(payload, url_path, headers)
|
|
425
|
+
|
|
426
|
+
self.logger.info(
|
|
427
|
+
f"Creating webhook for detected platform: {platform.value}"
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
# Create webhook instance using schema factory
|
|
431
|
+
webhook_instance = self.schema_factory.create_webhook_instance(
|
|
432
|
+
platform=platform, webhook_data=payload, **kwargs
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
self.logger.debug(
|
|
436
|
+
f"Created webhook instance: {webhook_instance.get_webhook_id()}"
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
return webhook_instance
|
|
440
|
+
|
|
441
|
+
except Exception as e:
|
|
442
|
+
self.logger.error(
|
|
443
|
+
f"Failed to create webhook from payload: {e}", exc_info=True
|
|
444
|
+
)
|
|
445
|
+
raise ProcessorError(
|
|
446
|
+
f"Failed to create webhook: {e}",
|
|
447
|
+
ErrorCode.PROCESSING_ERROR,
|
|
448
|
+
PlatformType.WHATSAPP, # Default for error reporting
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
def create_webhook_for_platform(
|
|
452
|
+
self, platform: PlatformType, payload: dict[str, Any], **kwargs
|
|
453
|
+
) -> BaseWebhook:
|
|
454
|
+
"""
|
|
455
|
+
Create a webhook instance for a specific platform.
|
|
456
|
+
|
|
457
|
+
Args:
|
|
458
|
+
platform: The platform to create webhook for
|
|
459
|
+
payload: Raw webhook payload dictionary
|
|
460
|
+
**kwargs: Additional parameters for webhook creation
|
|
461
|
+
|
|
462
|
+
Returns:
|
|
463
|
+
Parsed webhook instance with universal interface
|
|
464
|
+
|
|
465
|
+
Raises:
|
|
466
|
+
ValidationError: If webhook data is invalid
|
|
467
|
+
"""
|
|
468
|
+
try:
|
|
469
|
+
webhook_instance = self.schema_factory.create_webhook_instance(
|
|
470
|
+
platform=platform, webhook_data=payload, **kwargs
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
self.logger.debug(
|
|
474
|
+
f"Created {platform.value} webhook instance: {webhook_instance.get_webhook_id()}"
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
return webhook_instance
|
|
478
|
+
|
|
479
|
+
except Exception as e:
|
|
480
|
+
self.logger.error(
|
|
481
|
+
f"Failed to create {platform.value} webhook: {e}", exc_info=True
|
|
482
|
+
)
|
|
483
|
+
raise
|
|
484
|
+
|
|
485
|
+
def validate_webhook_payload(
|
|
486
|
+
self, payload: dict[str, Any], expected_platform: PlatformType | None = None
|
|
487
|
+
) -> tuple[bool, str | None, PlatformType | None]:
|
|
488
|
+
"""
|
|
489
|
+
Validate webhook payload and return platform information.
|
|
490
|
+
|
|
491
|
+
Args:
|
|
492
|
+
payload: Raw webhook payload dictionary
|
|
493
|
+
expected_platform: Optional expected platform for validation
|
|
494
|
+
|
|
495
|
+
Returns:
|
|
496
|
+
Tuple of (is_valid, error_message, detected_platform)
|
|
497
|
+
"""
|
|
498
|
+
try:
|
|
499
|
+
# Detect platform
|
|
500
|
+
detected_platform = PlatformDetector.detect_platform(payload)
|
|
501
|
+
|
|
502
|
+
# Check if detected platform matches expected platform
|
|
503
|
+
if expected_platform and detected_platform != expected_platform:
|
|
504
|
+
return (
|
|
505
|
+
False,
|
|
506
|
+
(
|
|
507
|
+
f"Platform mismatch: expected {expected_platform.value}, "
|
|
508
|
+
f"detected {detected_platform.value}"
|
|
509
|
+
),
|
|
510
|
+
detected_platform,
|
|
511
|
+
)
|
|
512
|
+
|
|
513
|
+
# Try to create webhook instance for validation
|
|
514
|
+
try:
|
|
515
|
+
self.schema_factory.create_webhook_instance(
|
|
516
|
+
platform=detected_platform, webhook_data=payload
|
|
517
|
+
)
|
|
518
|
+
return True, None, detected_platform
|
|
519
|
+
|
|
520
|
+
except Exception as e:
|
|
521
|
+
return False, f"Webhook validation failed: {e}", detected_platform
|
|
522
|
+
|
|
523
|
+
except Exception as e:
|
|
524
|
+
return False, f"Platform detection failed: {e}", None
|
|
525
|
+
|
|
526
|
+
def get_supported_platforms(self) -> set[PlatformType]:
|
|
527
|
+
"""
|
|
528
|
+
Get platforms supported by this factory.
|
|
529
|
+
|
|
530
|
+
Returns:
|
|
531
|
+
Set of supported platform types
|
|
532
|
+
"""
|
|
533
|
+
return self.schema_factory.webhook_registry.get_supported_platforms()
|
|
534
|
+
|
|
535
|
+
def is_platform_supported(self, platform: PlatformType) -> bool:
|
|
536
|
+
"""
|
|
537
|
+
Check if a platform is supported by this factory.
|
|
538
|
+
|
|
539
|
+
Args:
|
|
540
|
+
platform: Platform type to check
|
|
541
|
+
|
|
542
|
+
Returns:
|
|
543
|
+
True if platform is supported, False otherwise
|
|
544
|
+
"""
|
|
545
|
+
return self.schema_factory.webhook_registry.is_platform_supported(platform)
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
# Singleton instances for global access
|
|
549
|
+
processor_factory = ProcessorFactory()
|
|
550
|
+
webhook_factory = WebhookFactory()
|