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,754 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Schema factory for platform-agnostic message and webhook schema creation.
|
|
3
|
+
|
|
4
|
+
This module provides factory classes for dynamically selecting and creating
|
|
5
|
+
the appropriate schema classes based on platform and message type combinations.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from wappa.core.logging.logger import get_logger
|
|
11
|
+
from wappa.webhooks.core.base_message import BaseMessage
|
|
12
|
+
from wappa.webhooks.core.base_webhook import BaseWebhook
|
|
13
|
+
from wappa.webhooks.core.types import MessageType, PlatformType
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SchemaRegistryError(Exception):
|
|
17
|
+
"""Raised when schema registry operations fail."""
|
|
18
|
+
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class MessageSchemaRegistry:
|
|
23
|
+
"""
|
|
24
|
+
Registry for message schema classes organized by platform and message type.
|
|
25
|
+
|
|
26
|
+
Provides centralized registration and lookup of message schema classes
|
|
27
|
+
to enable dynamic schema selection based on incoming webhook data.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self):
|
|
31
|
+
"""Initialize the schema registry."""
|
|
32
|
+
self.logger = get_logger(__name__)
|
|
33
|
+
|
|
34
|
+
# Registry structure: {platform: {message_type: schema_class}}
|
|
35
|
+
self._message_schemas: dict[
|
|
36
|
+
PlatformType, dict[MessageType, type[BaseMessage]]
|
|
37
|
+
] = {}
|
|
38
|
+
|
|
39
|
+
# Initialize with WhatsApp schemas
|
|
40
|
+
self._register_whatsapp_schemas()
|
|
41
|
+
|
|
42
|
+
def _register_whatsapp_schemas(self) -> None:
|
|
43
|
+
"""Register all WhatsApp message schema classes."""
|
|
44
|
+
try:
|
|
45
|
+
# Import WhatsApp message schemas
|
|
46
|
+
from .whatsapp.message_types.audio import WhatsAppAudioMessage
|
|
47
|
+
from .whatsapp.message_types.button import WhatsAppButtonMessage
|
|
48
|
+
from .whatsapp.message_types.contact import WhatsAppContactMessage
|
|
49
|
+
from .whatsapp.message_types.document import WhatsAppDocumentMessage
|
|
50
|
+
from .whatsapp.message_types.image import WhatsAppImageMessage
|
|
51
|
+
from .whatsapp.message_types.interactive import WhatsAppInteractiveMessage
|
|
52
|
+
from .whatsapp.message_types.location import WhatsAppLocationMessage
|
|
53
|
+
from .whatsapp.message_types.order import WhatsAppOrderMessage
|
|
54
|
+
from .whatsapp.message_types.reaction import WhatsAppReactionMessage
|
|
55
|
+
from .whatsapp.message_types.sticker import WhatsAppStickerMessage
|
|
56
|
+
from .whatsapp.message_types.system import WhatsAppSystemMessage
|
|
57
|
+
from .whatsapp.message_types.text import WhatsAppTextMessage
|
|
58
|
+
from .whatsapp.message_types.unsupported import WhatsAppUnsupportedMessage
|
|
59
|
+
from .whatsapp.message_types.video import WhatsAppVideoMessage
|
|
60
|
+
|
|
61
|
+
# Register WhatsApp schemas
|
|
62
|
+
whatsapp_schemas = {
|
|
63
|
+
MessageType.TEXT: WhatsAppTextMessage,
|
|
64
|
+
MessageType.INTERACTIVE: WhatsAppInteractiveMessage,
|
|
65
|
+
MessageType.IMAGE: WhatsAppImageMessage,
|
|
66
|
+
MessageType.AUDIO: WhatsAppAudioMessage,
|
|
67
|
+
MessageType.VIDEO: WhatsAppVideoMessage,
|
|
68
|
+
MessageType.DOCUMENT: WhatsAppDocumentMessage,
|
|
69
|
+
MessageType.CONTACT: WhatsAppContactMessage,
|
|
70
|
+
MessageType.LOCATION: WhatsAppLocationMessage,
|
|
71
|
+
MessageType.STICKER: WhatsAppStickerMessage,
|
|
72
|
+
MessageType.SYSTEM: WhatsAppSystemMessage,
|
|
73
|
+
MessageType.REACTION: WhatsAppReactionMessage,
|
|
74
|
+
# Additional WhatsApp-specific types (now with proper enum values)
|
|
75
|
+
MessageType.BUTTON: WhatsAppButtonMessage,
|
|
76
|
+
MessageType.ORDER: WhatsAppOrderMessage,
|
|
77
|
+
MessageType.UNSUPPORTED: WhatsAppUnsupportedMessage,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
self._message_schemas[PlatformType.WHATSAPP] = whatsapp_schemas
|
|
81
|
+
|
|
82
|
+
self.logger.info(
|
|
83
|
+
f"Registered {len(whatsapp_schemas)} WhatsApp message schemas"
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
except ImportError as e:
|
|
87
|
+
self.logger.error(f"Failed to import WhatsApp schemas: {e}")
|
|
88
|
+
raise SchemaRegistryError(f"Failed to register WhatsApp schemas: {e}")
|
|
89
|
+
|
|
90
|
+
def register_message_schema(
|
|
91
|
+
self,
|
|
92
|
+
platform: PlatformType,
|
|
93
|
+
message_type: MessageType,
|
|
94
|
+
schema_class: type[BaseMessage],
|
|
95
|
+
) -> None:
|
|
96
|
+
"""
|
|
97
|
+
Register a message schema class for a platform and message type.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
platform: The platform this schema belongs to
|
|
101
|
+
message_type: The message type this schema handles
|
|
102
|
+
schema_class: The Pydantic model class for this message type
|
|
103
|
+
|
|
104
|
+
Raises:
|
|
105
|
+
SchemaRegistryError: If registration fails
|
|
106
|
+
"""
|
|
107
|
+
if not issubclass(schema_class, BaseMessage):
|
|
108
|
+
raise SchemaRegistryError(
|
|
109
|
+
f"Schema class must inherit from BaseMessage: {schema_class}"
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
if platform not in self._message_schemas:
|
|
113
|
+
self._message_schemas[platform] = {}
|
|
114
|
+
|
|
115
|
+
self._message_schemas[platform][message_type] = schema_class
|
|
116
|
+
|
|
117
|
+
self.logger.debug(
|
|
118
|
+
f"Registered schema: {platform.value}.{message_type.value} -> {schema_class.__name__}"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
def get_message_schema(
|
|
122
|
+
self, platform: PlatformType, message_type: MessageType
|
|
123
|
+
) -> type[BaseMessage] | None:
|
|
124
|
+
"""
|
|
125
|
+
Get the message schema class for a platform and message type.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
platform: The platform to get schema for
|
|
129
|
+
message_type: The message type to get schema for
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Message schema class or None if not found
|
|
133
|
+
"""
|
|
134
|
+
platform_schemas = self._message_schemas.get(platform, {})
|
|
135
|
+
return platform_schemas.get(message_type)
|
|
136
|
+
|
|
137
|
+
def is_message_type_supported(
|
|
138
|
+
self, platform: PlatformType, message_type: MessageType
|
|
139
|
+
) -> bool:
|
|
140
|
+
"""
|
|
141
|
+
Check if a message type is supported for a platform.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
platform: The platform to check
|
|
145
|
+
message_type: The message type to check
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
True if supported, False otherwise
|
|
149
|
+
"""
|
|
150
|
+
return self.get_message_schema(platform, message_type) is not None
|
|
151
|
+
|
|
152
|
+
def get_supported_message_types(self, platform: PlatformType) -> set[MessageType]:
|
|
153
|
+
"""
|
|
154
|
+
Get all supported message types for a platform.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
platform: The platform to get supported types for
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
Set of supported message types
|
|
161
|
+
"""
|
|
162
|
+
platform_schemas = self._message_schemas.get(platform, {})
|
|
163
|
+
return set(platform_schemas.keys())
|
|
164
|
+
|
|
165
|
+
def get_supported_platforms(self) -> set[PlatformType]:
|
|
166
|
+
"""
|
|
167
|
+
Get all platforms with registered schemas.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Set of platforms with registered schemas
|
|
171
|
+
"""
|
|
172
|
+
return set(self._message_schemas.keys())
|
|
173
|
+
|
|
174
|
+
def get_registry_stats(self) -> dict[str, Any]:
|
|
175
|
+
"""
|
|
176
|
+
Get registry statistics for monitoring.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Dictionary with registry statistics
|
|
180
|
+
"""
|
|
181
|
+
stats = {"total_platforms": len(self._message_schemas), "platforms": {}}
|
|
182
|
+
|
|
183
|
+
for platform, schemas in self._message_schemas.items():
|
|
184
|
+
stats["platforms"][platform.value] = {
|
|
185
|
+
"message_types": len(schemas),
|
|
186
|
+
"supported_types": [mt.value for mt in schemas.keys()],
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return stats
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class WebhookSchemaRegistry:
|
|
193
|
+
"""
|
|
194
|
+
Registry for webhook container schema classes organized by platform.
|
|
195
|
+
|
|
196
|
+
Provides centralized registration and lookup of webhook container schemas
|
|
197
|
+
for parsing platform-specific webhook structures.
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
def __init__(self):
|
|
201
|
+
"""Initialize the webhook schema registry."""
|
|
202
|
+
self.logger = get_logger(__name__)
|
|
203
|
+
|
|
204
|
+
# Registry structure: {platform: webhook_schema_class}
|
|
205
|
+
self._webhook_schemas: dict[PlatformType, type[BaseWebhook]] = {}
|
|
206
|
+
|
|
207
|
+
# Initialize with WhatsApp webhook schema
|
|
208
|
+
self._register_whatsapp_webhook_schema()
|
|
209
|
+
|
|
210
|
+
def _register_whatsapp_webhook_schema(self) -> None:
|
|
211
|
+
"""Register WhatsApp webhook container schema."""
|
|
212
|
+
try:
|
|
213
|
+
from .whatsapp.webhook_container import WhatsAppWebhook
|
|
214
|
+
|
|
215
|
+
self._webhook_schemas[PlatformType.WHATSAPP] = WhatsAppWebhook
|
|
216
|
+
|
|
217
|
+
self.logger.info("Registered WhatsApp webhook schema")
|
|
218
|
+
|
|
219
|
+
except ImportError as e:
|
|
220
|
+
self.logger.error(f"Failed to import WhatsApp webhook schema: {e}")
|
|
221
|
+
raise SchemaRegistryError(
|
|
222
|
+
f"Failed to register WhatsApp webhook schema: {e}"
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
def register_webhook_schema(
|
|
226
|
+
self, platform: PlatformType, schema_class: type[BaseWebhook]
|
|
227
|
+
) -> None:
|
|
228
|
+
"""
|
|
229
|
+
Register a webhook schema class for a platform.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
platform: The platform this schema belongs to
|
|
233
|
+
schema_class: The Pydantic model class for webhook containers
|
|
234
|
+
|
|
235
|
+
Raises:
|
|
236
|
+
SchemaRegistryError: If registration fails
|
|
237
|
+
"""
|
|
238
|
+
if not issubclass(schema_class, BaseWebhook):
|
|
239
|
+
raise SchemaRegistryError(
|
|
240
|
+
f"Schema class must inherit from BaseWebhook: {schema_class}"
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
self._webhook_schemas[platform] = schema_class
|
|
244
|
+
|
|
245
|
+
self.logger.debug(
|
|
246
|
+
f"Registered webhook schema: {platform.value} -> {schema_class.__name__}"
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
def get_webhook_schema(self, platform: PlatformType) -> type[BaseWebhook] | None:
|
|
250
|
+
"""
|
|
251
|
+
Get the webhook schema class for a platform.
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
platform: The platform to get schema for
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
Webhook schema class or None if not found
|
|
258
|
+
"""
|
|
259
|
+
return self._webhook_schemas.get(platform)
|
|
260
|
+
|
|
261
|
+
def is_platform_supported(self, platform: PlatformType) -> bool:
|
|
262
|
+
"""
|
|
263
|
+
Check if a platform has a registered webhook schema.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
platform: The platform to check
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
True if supported, False otherwise
|
|
270
|
+
"""
|
|
271
|
+
return platform in self._webhook_schemas
|
|
272
|
+
|
|
273
|
+
def get_supported_platforms(self) -> set[PlatformType]:
|
|
274
|
+
"""
|
|
275
|
+
Get all platforms with registered webhook schemas.
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
Set of supported platforms
|
|
279
|
+
"""
|
|
280
|
+
return set(self._webhook_schemas.keys())
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class SchemaFactory:
|
|
284
|
+
"""
|
|
285
|
+
Factory for creating platform-specific message and webhook schema instances.
|
|
286
|
+
|
|
287
|
+
Provides centralized schema selection and instantiation based on platform
|
|
288
|
+
and message type, with comprehensive error handling and validation.
|
|
289
|
+
"""
|
|
290
|
+
|
|
291
|
+
def __init__(self):
|
|
292
|
+
"""Initialize the schema factory with registries."""
|
|
293
|
+
self.logger = get_logger(__name__)
|
|
294
|
+
|
|
295
|
+
# Initialize registries
|
|
296
|
+
self.message_registry = MessageSchemaRegistry()
|
|
297
|
+
self.webhook_registry = WebhookSchemaRegistry()
|
|
298
|
+
|
|
299
|
+
def create_message_instance(
|
|
300
|
+
self,
|
|
301
|
+
platform: PlatformType,
|
|
302
|
+
message_type: MessageType,
|
|
303
|
+
message_data: dict[str, Any],
|
|
304
|
+
**kwargs,
|
|
305
|
+
) -> BaseMessage:
|
|
306
|
+
"""
|
|
307
|
+
Create a message instance from raw data.
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
platform: The platform this message came from
|
|
311
|
+
message_type: The type of message to create
|
|
312
|
+
message_data: Raw message data from webhook
|
|
313
|
+
**kwargs: Additional parameters for message creation
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
Parsed message instance with universal interface
|
|
317
|
+
|
|
318
|
+
Raises:
|
|
319
|
+
SchemaRegistryError: If schema not found for platform/message type
|
|
320
|
+
ValidationError: If message data is invalid
|
|
321
|
+
"""
|
|
322
|
+
# Get appropriate schema class
|
|
323
|
+
schema_class = self.message_registry.get_message_schema(platform, message_type)
|
|
324
|
+
|
|
325
|
+
if schema_class is None:
|
|
326
|
+
raise SchemaRegistryError(
|
|
327
|
+
f"No schema registered for {platform.value}.{message_type.value}"
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
try:
|
|
331
|
+
# Create and validate message instance
|
|
332
|
+
message_instance = schema_class.model_validate(message_data)
|
|
333
|
+
|
|
334
|
+
self.logger.debug(
|
|
335
|
+
f"Created {schema_class.__name__} instance: {message_instance.message_id}"
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
return message_instance
|
|
339
|
+
|
|
340
|
+
except Exception as e:
|
|
341
|
+
self.logger.error(
|
|
342
|
+
f"Failed to create {schema_class.__name__} instance: {e}", exc_info=True
|
|
343
|
+
)
|
|
344
|
+
raise
|
|
345
|
+
|
|
346
|
+
def create_webhook_instance(
|
|
347
|
+
self, platform: PlatformType, webhook_data: dict[str, Any], **kwargs
|
|
348
|
+
) -> BaseWebhook:
|
|
349
|
+
"""
|
|
350
|
+
Create a webhook container instance from raw data.
|
|
351
|
+
|
|
352
|
+
Args:
|
|
353
|
+
platform: The platform this webhook came from
|
|
354
|
+
webhook_data: Raw webhook payload data
|
|
355
|
+
**kwargs: Additional parameters for webhook creation
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
Parsed webhook instance with universal interface
|
|
359
|
+
|
|
360
|
+
Raises:
|
|
361
|
+
SchemaRegistryError: If schema not found for platform
|
|
362
|
+
ValidationError: If webhook data is invalid
|
|
363
|
+
"""
|
|
364
|
+
# Get appropriate webhook schema class
|
|
365
|
+
schema_class = self.webhook_registry.get_webhook_schema(platform)
|
|
366
|
+
|
|
367
|
+
if schema_class is None:
|
|
368
|
+
raise SchemaRegistryError(
|
|
369
|
+
f"No webhook schema registered for {platform.value}"
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
try:
|
|
373
|
+
# Create and validate webhook instance
|
|
374
|
+
webhook_instance = schema_class.model_validate(webhook_data)
|
|
375
|
+
|
|
376
|
+
self.logger.debug(
|
|
377
|
+
f"Created {schema_class.__name__} instance: {webhook_instance.get_webhook_id()}"
|
|
378
|
+
)
|
|
379
|
+
|
|
380
|
+
return webhook_instance
|
|
381
|
+
|
|
382
|
+
except Exception as e:
|
|
383
|
+
self.logger.error(
|
|
384
|
+
f"Failed to create {schema_class.__name__} instance: {e}", exc_info=True
|
|
385
|
+
)
|
|
386
|
+
raise
|
|
387
|
+
|
|
388
|
+
def detect_message_type(
|
|
389
|
+
self, platform: PlatformType, message_data: dict[str, Any]
|
|
390
|
+
) -> MessageType | None:
|
|
391
|
+
"""
|
|
392
|
+
Detect message type from raw message data.
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
platform: The platform this message came from
|
|
396
|
+
message_data: Raw message data
|
|
397
|
+
|
|
398
|
+
Returns:
|
|
399
|
+
Detected message type or None if cannot determine
|
|
400
|
+
"""
|
|
401
|
+
if not isinstance(message_data, dict):
|
|
402
|
+
return None
|
|
403
|
+
|
|
404
|
+
# Get type from message data (common pattern across platforms)
|
|
405
|
+
message_type_str = message_data.get("type")
|
|
406
|
+
if not message_type_str:
|
|
407
|
+
return None
|
|
408
|
+
|
|
409
|
+
try:
|
|
410
|
+
# Try to convert to MessageType enum
|
|
411
|
+
message_type = MessageType(message_type_str)
|
|
412
|
+
|
|
413
|
+
# Verify this message type is supported for the platform
|
|
414
|
+
if self.message_registry.is_message_type_supported(platform, message_type):
|
|
415
|
+
return message_type
|
|
416
|
+
|
|
417
|
+
except ValueError:
|
|
418
|
+
# Handle platform-specific message types not in the enum
|
|
419
|
+
if platform == PlatformType.WHATSAPP:
|
|
420
|
+
# WhatsApp-specific message types
|
|
421
|
+
whatsapp_types = {
|
|
422
|
+
"button": MessageType.INTERACTIVE, # Map to closest standard type
|
|
423
|
+
"order": MessageType.INTERACTIVE, # Map to closest standard type
|
|
424
|
+
"unsupported": MessageType.SYSTEM, # Map to closest standard type
|
|
425
|
+
}
|
|
426
|
+
mapped_type = whatsapp_types.get(message_type_str)
|
|
427
|
+
if mapped_type and self.message_registry.is_message_type_supported(
|
|
428
|
+
platform, mapped_type
|
|
429
|
+
):
|
|
430
|
+
return mapped_type
|
|
431
|
+
|
|
432
|
+
return None
|
|
433
|
+
|
|
434
|
+
def get_supported_combinations(self) -> dict[str, list[str]]:
|
|
435
|
+
"""
|
|
436
|
+
Get all supported platform and message type combinations.
|
|
437
|
+
|
|
438
|
+
Returns:
|
|
439
|
+
Dictionary mapping platform names to lists of supported message types
|
|
440
|
+
"""
|
|
441
|
+
combinations = {}
|
|
442
|
+
|
|
443
|
+
for platform in self.message_registry.get_supported_platforms():
|
|
444
|
+
message_types = self.message_registry.get_supported_message_types(platform)
|
|
445
|
+
combinations[platform.value] = [mt.value for mt in message_types]
|
|
446
|
+
|
|
447
|
+
return combinations
|
|
448
|
+
|
|
449
|
+
def validate_platform_message_compatibility(
|
|
450
|
+
self, platform: PlatformType, message_type: MessageType
|
|
451
|
+
) -> tuple[bool, str | None]:
|
|
452
|
+
"""
|
|
453
|
+
Validate if a platform supports a specific message type.
|
|
454
|
+
|
|
455
|
+
Args:
|
|
456
|
+
platform: The platform to check
|
|
457
|
+
message_type: The message type to check
|
|
458
|
+
|
|
459
|
+
Returns:
|
|
460
|
+
Tuple of (is_supported, error_message)
|
|
461
|
+
"""
|
|
462
|
+
if not self.message_registry.is_message_type_supported(platform, message_type):
|
|
463
|
+
supported_types = self.message_registry.get_supported_message_types(
|
|
464
|
+
platform
|
|
465
|
+
)
|
|
466
|
+
return False, (
|
|
467
|
+
f"Message type '{message_type.value}' not supported for platform '{platform.value}'. "
|
|
468
|
+
f"Supported types: {[mt.value for mt in supported_types]}"
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
return True, None
|
|
472
|
+
|
|
473
|
+
def get_factory_stats(self) -> dict[str, Any]:
|
|
474
|
+
"""
|
|
475
|
+
Get factory statistics for monitoring.
|
|
476
|
+
|
|
477
|
+
Returns:
|
|
478
|
+
Dictionary with factory statistics
|
|
479
|
+
"""
|
|
480
|
+
return {
|
|
481
|
+
"message_registry": self.message_registry.get_registry_stats(),
|
|
482
|
+
"webhook_registry": {
|
|
483
|
+
"supported_platforms": [
|
|
484
|
+
p.value for p in self.webhook_registry.get_supported_platforms()
|
|
485
|
+
]
|
|
486
|
+
},
|
|
487
|
+
"total_combinations": sum(
|
|
488
|
+
len(types) for types in self.get_supported_combinations().values()
|
|
489
|
+
),
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
# ===== Universal Webhook Interface Support =====
|
|
493
|
+
|
|
494
|
+
def detect_webhook_type(self, payload: dict[str, Any]) -> str | None:
|
|
495
|
+
"""
|
|
496
|
+
Detect webhook type from raw payload structure.
|
|
497
|
+
|
|
498
|
+
Based on WhatsApp webhook structure but designed to be universal:
|
|
499
|
+
- IncomingMessage: Contains 'messages' array
|
|
500
|
+
- Status: Contains 'statuses' array
|
|
501
|
+
- Error: Contains 'errors' array at value level
|
|
502
|
+
- OutgoingMessage: Future - currently not supported
|
|
503
|
+
|
|
504
|
+
Args:
|
|
505
|
+
payload: Raw webhook payload dictionary
|
|
506
|
+
|
|
507
|
+
Returns:
|
|
508
|
+
Webhook type string or None if cannot determine
|
|
509
|
+
"""
|
|
510
|
+
if not isinstance(payload, dict):
|
|
511
|
+
return None
|
|
512
|
+
|
|
513
|
+
# Navigate to WhatsApp webhook structure: entry[0].changes[0].value
|
|
514
|
+
try:
|
|
515
|
+
entry = payload.get("entry", [])
|
|
516
|
+
if not entry or not isinstance(entry, list):
|
|
517
|
+
return None
|
|
518
|
+
|
|
519
|
+
changes = entry[0].get("changes", [])
|
|
520
|
+
if not changes or not isinstance(changes, list):
|
|
521
|
+
return None
|
|
522
|
+
|
|
523
|
+
value = changes[0].get("value", {})
|
|
524
|
+
if not isinstance(value, dict):
|
|
525
|
+
return None
|
|
526
|
+
|
|
527
|
+
# Check for webhook type indicators
|
|
528
|
+
if (
|
|
529
|
+
"messages" in value
|
|
530
|
+
and isinstance(value["messages"], list)
|
|
531
|
+
and len(value["messages"]) > 0
|
|
532
|
+
):
|
|
533
|
+
return "incoming_message"
|
|
534
|
+
elif (
|
|
535
|
+
"statuses" in value
|
|
536
|
+
and isinstance(value["statuses"], list)
|
|
537
|
+
and len(value["statuses"]) > 0
|
|
538
|
+
):
|
|
539
|
+
return "status"
|
|
540
|
+
elif (
|
|
541
|
+
"errors" in value
|
|
542
|
+
and isinstance(value["errors"], list)
|
|
543
|
+
and len(value["errors"]) > 0
|
|
544
|
+
):
|
|
545
|
+
return "error"
|
|
546
|
+
|
|
547
|
+
return None
|
|
548
|
+
|
|
549
|
+
except (KeyError, IndexError, TypeError):
|
|
550
|
+
return None
|
|
551
|
+
|
|
552
|
+
async def create_universal_webhook(
|
|
553
|
+
self,
|
|
554
|
+
platform: PlatformType,
|
|
555
|
+
payload: dict[str, Any],
|
|
556
|
+
webhook_type: str | None = None,
|
|
557
|
+
**kwargs,
|
|
558
|
+
) -> "UniversalWebhook":
|
|
559
|
+
"""
|
|
560
|
+
Create a Universal Webhook Interface from platform-specific payload.
|
|
561
|
+
|
|
562
|
+
This method acts as the main factory for converting platform webhooks
|
|
563
|
+
into universal interfaces. It delegates to platform-specific processors
|
|
564
|
+
to handle the transformation.
|
|
565
|
+
|
|
566
|
+
Args:
|
|
567
|
+
platform: The platform this webhook came from
|
|
568
|
+
payload: Raw webhook payload dictionary
|
|
569
|
+
webhook_type: Optional webhook type hint (will auto-detect if None)
|
|
570
|
+
**kwargs: Additional parameters for webhook creation
|
|
571
|
+
|
|
572
|
+
Returns:
|
|
573
|
+
Universal webhook interface instance
|
|
574
|
+
|
|
575
|
+
Raises:
|
|
576
|
+
SchemaRegistryError: If platform not supported or conversion fails
|
|
577
|
+
"""
|
|
578
|
+
from wappa.processors.factory import processor_factory
|
|
579
|
+
|
|
580
|
+
try:
|
|
581
|
+
# Get platform-specific processor
|
|
582
|
+
processor = processor_factory.get_processor(platform)
|
|
583
|
+
|
|
584
|
+
# Use processor to create universal webhook
|
|
585
|
+
if hasattr(processor, "create_universal_webhook"):
|
|
586
|
+
return await processor.create_universal_webhook(payload, **kwargs)
|
|
587
|
+
else:
|
|
588
|
+
raise SchemaRegistryError(
|
|
589
|
+
f"Processor for {platform.value} does not support universal webhook creation"
|
|
590
|
+
)
|
|
591
|
+
|
|
592
|
+
except Exception as e:
|
|
593
|
+
self.logger.error(
|
|
594
|
+
f"Failed to create universal webhook for {platform.value}: {e}",
|
|
595
|
+
exc_info=True,
|
|
596
|
+
)
|
|
597
|
+
raise SchemaRegistryError(f"Failed to create universal webhook: {e}")
|
|
598
|
+
|
|
599
|
+
def create_universal_webhook_from_payload(
|
|
600
|
+
self,
|
|
601
|
+
payload: dict[str, Any],
|
|
602
|
+
url_path: str | None = None,
|
|
603
|
+
headers: dict[str, str] | None = None,
|
|
604
|
+
**kwargs,
|
|
605
|
+
) -> "UniversalWebhook":
|
|
606
|
+
"""
|
|
607
|
+
Create Universal Webhook Interface with automatic platform detection.
|
|
608
|
+
|
|
609
|
+
Args:
|
|
610
|
+
payload: Raw webhook payload dictionary
|
|
611
|
+
url_path: Optional URL path for platform detection
|
|
612
|
+
headers: Optional HTTP headers for platform detection
|
|
613
|
+
**kwargs: Additional parameters for webhook creation
|
|
614
|
+
|
|
615
|
+
Returns:
|
|
616
|
+
Universal webhook interface instance
|
|
617
|
+
|
|
618
|
+
Raises:
|
|
619
|
+
SchemaRegistryError: If platform cannot be detected or conversion fails
|
|
620
|
+
"""
|
|
621
|
+
from wappa.processors.factory import PlatformDetector
|
|
622
|
+
|
|
623
|
+
try:
|
|
624
|
+
# Detect platform from payload
|
|
625
|
+
platform = PlatformDetector.detect_platform(payload, url_path, headers)
|
|
626
|
+
|
|
627
|
+
# Create universal webhook for detected platform
|
|
628
|
+
return self.create_universal_webhook(platform, payload, **kwargs)
|
|
629
|
+
|
|
630
|
+
except Exception as e:
|
|
631
|
+
self.logger.error(
|
|
632
|
+
f"Failed to create universal webhook from payload: {e}", exc_info=True
|
|
633
|
+
)
|
|
634
|
+
raise SchemaRegistryError(f"Failed to create universal webhook: {e}")
|
|
635
|
+
|
|
636
|
+
def validate_universal_webhook_payload(
|
|
637
|
+
self, payload: dict[str, Any], expected_type: str | None = None
|
|
638
|
+
) -> tuple[bool, str | None, str | None]:
|
|
639
|
+
"""
|
|
640
|
+
Validate payload for universal webhook creation.
|
|
641
|
+
|
|
642
|
+
Args:
|
|
643
|
+
payload: Raw webhook payload dictionary
|
|
644
|
+
expected_type: Optional expected webhook type for validation
|
|
645
|
+
|
|
646
|
+
Returns:
|
|
647
|
+
Tuple of (is_valid, error_message, detected_type)
|
|
648
|
+
"""
|
|
649
|
+
try:
|
|
650
|
+
# Detect webhook type
|
|
651
|
+
detected_type = self.detect_webhook_type(payload)
|
|
652
|
+
|
|
653
|
+
if not detected_type:
|
|
654
|
+
return (
|
|
655
|
+
False,
|
|
656
|
+
"Unable to determine webhook type from payload structure",
|
|
657
|
+
None,
|
|
658
|
+
)
|
|
659
|
+
|
|
660
|
+
# Check if detected type matches expected
|
|
661
|
+
if expected_type and detected_type != expected_type:
|
|
662
|
+
return (
|
|
663
|
+
False,
|
|
664
|
+
(
|
|
665
|
+
f"Webhook type mismatch: expected {expected_type}, "
|
|
666
|
+
f"detected {detected_type}"
|
|
667
|
+
),
|
|
668
|
+
detected_type,
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
# Basic structure validation
|
|
672
|
+
if not self._validate_basic_webhook_structure(payload):
|
|
673
|
+
return (
|
|
674
|
+
False,
|
|
675
|
+
"Payload does not match expected webhook structure",
|
|
676
|
+
detected_type,
|
|
677
|
+
)
|
|
678
|
+
|
|
679
|
+
return True, None, detected_type
|
|
680
|
+
|
|
681
|
+
except Exception as e:
|
|
682
|
+
return False, f"Validation error: {e}", None
|
|
683
|
+
|
|
684
|
+
def _validate_basic_webhook_structure(self, payload: dict[str, Any]) -> bool:
|
|
685
|
+
"""
|
|
686
|
+
Validate basic webhook structure (based on WhatsApp format).
|
|
687
|
+
|
|
688
|
+
Args:
|
|
689
|
+
payload: Raw webhook payload dictionary
|
|
690
|
+
|
|
691
|
+
Returns:
|
|
692
|
+
True if structure is valid, False otherwise
|
|
693
|
+
"""
|
|
694
|
+
try:
|
|
695
|
+
# Check for required top-level fields
|
|
696
|
+
if "object" not in payload or "entry" not in payload:
|
|
697
|
+
return False
|
|
698
|
+
|
|
699
|
+
# Check entry structure
|
|
700
|
+
entry = payload["entry"]
|
|
701
|
+
if not isinstance(entry, list) or len(entry) == 0:
|
|
702
|
+
return False
|
|
703
|
+
|
|
704
|
+
# Check changes structure
|
|
705
|
+
first_entry = entry[0]
|
|
706
|
+
if "changes" not in first_entry:
|
|
707
|
+
return False
|
|
708
|
+
|
|
709
|
+
changes = first_entry["changes"]
|
|
710
|
+
if not isinstance(changes, list) or len(changes) == 0:
|
|
711
|
+
return False
|
|
712
|
+
|
|
713
|
+
# Check value structure
|
|
714
|
+
first_change = changes[0]
|
|
715
|
+
if "value" not in first_change:
|
|
716
|
+
return False
|
|
717
|
+
|
|
718
|
+
value = first_change["value"]
|
|
719
|
+
if not isinstance(value, dict):
|
|
720
|
+
return False
|
|
721
|
+
|
|
722
|
+
# Should have metadata at minimum
|
|
723
|
+
if "metadata" not in value:
|
|
724
|
+
return False
|
|
725
|
+
|
|
726
|
+
return True
|
|
727
|
+
|
|
728
|
+
except (KeyError, IndexError, TypeError):
|
|
729
|
+
return False
|
|
730
|
+
|
|
731
|
+
def get_supported_webhook_types(self) -> list[str]:
|
|
732
|
+
"""
|
|
733
|
+
Get list of supported universal webhook types.
|
|
734
|
+
|
|
735
|
+
Returns:
|
|
736
|
+
List of supported webhook type strings
|
|
737
|
+
"""
|
|
738
|
+
return ["incoming_message", "status", "error", "outgoing_message"]
|
|
739
|
+
|
|
740
|
+
def is_webhook_type_supported(self, webhook_type: str) -> bool:
|
|
741
|
+
"""
|
|
742
|
+
Check if a webhook type is supported.
|
|
743
|
+
|
|
744
|
+
Args:
|
|
745
|
+
webhook_type: Webhook type string to check
|
|
746
|
+
|
|
747
|
+
Returns:
|
|
748
|
+
True if supported, False otherwise
|
|
749
|
+
"""
|
|
750
|
+
return webhook_type in self.get_supported_webhook_types()
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
# Singleton instance for global access
|
|
754
|
+
schema_factory = SchemaFactory()
|