wappa 0.1.9__py3-none-any.whl → 0.1.10__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 +4 -5
- wappa/api/controllers/webhook_controller.py +5 -2
- wappa/api/dependencies/__init__.py +0 -5
- wappa/api/middleware/error_handler.py +4 -4
- wappa/api/middleware/owner.py +11 -5
- wappa/api/routes/webhooks.py +2 -2
- wappa/cli/__init__.py +1 -1
- wappa/cli/examples/init/app/main.py +2 -1
- wappa/cli/examples/init/app/master_event.py +5 -3
- wappa/cli/examples/json_cache_example/app/__init__.py +1 -1
- wappa/cli/examples/json_cache_example/app/main.py +56 -44
- wappa/cli/examples/json_cache_example/app/master_event.py +181 -145
- wappa/cli/examples/json_cache_example/app/models/__init__.py +1 -1
- wappa/cli/examples/json_cache_example/app/models/json_demo_models.py +32 -51
- wappa/cli/examples/json_cache_example/app/scores/__init__.py +2 -2
- wappa/cli/examples/json_cache_example/app/scores/score_base.py +52 -46
- wappa/cli/examples/json_cache_example/app/scores/score_cache_statistics.py +70 -62
- wappa/cli/examples/json_cache_example/app/scores/score_message_history.py +41 -44
- wappa/cli/examples/json_cache_example/app/scores/score_state_commands.py +83 -71
- wappa/cli/examples/json_cache_example/app/scores/score_user_management.py +73 -57
- wappa/cli/examples/json_cache_example/app/utils/__init__.py +2 -2
- wappa/cli/examples/json_cache_example/app/utils/cache_utils.py +54 -56
- wappa/cli/examples/json_cache_example/app/utils/message_utils.py +85 -80
- wappa/cli/examples/openai_transcript/app/main.py +2 -1
- wappa/cli/examples/openai_transcript/app/master_event.py +31 -22
- wappa/cli/examples/openai_transcript/app/openai_utils/__init__.py +1 -1
- wappa/cli/examples/openai_transcript/app/openai_utils/audio_processing.py +37 -24
- wappa/cli/examples/redis_cache_example/app/__init__.py +1 -1
- wappa/cli/examples/redis_cache_example/app/main.py +56 -44
- wappa/cli/examples/redis_cache_example/app/master_event.py +181 -145
- wappa/cli/examples/redis_cache_example/app/models/redis_demo_models.py +31 -50
- wappa/cli/examples/redis_cache_example/app/scores/__init__.py +2 -2
- wappa/cli/examples/redis_cache_example/app/scores/score_base.py +52 -46
- wappa/cli/examples/redis_cache_example/app/scores/score_cache_statistics.py +70 -62
- wappa/cli/examples/redis_cache_example/app/scores/score_message_history.py +41 -44
- wappa/cli/examples/redis_cache_example/app/scores/score_state_commands.py +83 -71
- wappa/cli/examples/redis_cache_example/app/scores/score_user_management.py +73 -57
- wappa/cli/examples/redis_cache_example/app/utils/__init__.py +2 -2
- wappa/cli/examples/redis_cache_example/app/utils/cache_utils.py +54 -56
- wappa/cli/examples/redis_cache_example/app/utils/message_utils.py +85 -80
- wappa/cli/examples/simple_echo_example/app/__init__.py +1 -1
- wappa/cli/examples/simple_echo_example/app/main.py +41 -33
- wappa/cli/examples/simple_echo_example/app/master_event.py +78 -57
- wappa/cli/examples/wappa_full_example/app/__init__.py +1 -1
- wappa/cli/examples/wappa_full_example/app/handlers/__init__.py +1 -1
- wappa/cli/examples/wappa_full_example/app/handlers/command_handlers.py +134 -126
- wappa/cli/examples/wappa_full_example/app/handlers/message_handlers.py +237 -229
- wappa/cli/examples/wappa_full_example/app/handlers/state_handlers.py +170 -148
- wappa/cli/examples/wappa_full_example/app/main.py +51 -39
- wappa/cli/examples/wappa_full_example/app/master_event.py +179 -120
- wappa/cli/examples/wappa_full_example/app/models/__init__.py +1 -1
- wappa/cli/examples/wappa_full_example/app/models/state_models.py +113 -104
- wappa/cli/examples/wappa_full_example/app/models/user_models.py +92 -76
- wappa/cli/examples/wappa_full_example/app/models/webhook_metadata.py +109 -83
- wappa/cli/examples/wappa_full_example/app/utils/__init__.py +1 -1
- wappa/cli/examples/wappa_full_example/app/utils/cache_utils.py +132 -113
- wappa/cli/examples/wappa_full_example/app/utils/media_handler.py +175 -132
- wappa/cli/examples/wappa_full_example/app/utils/metadata_extractor.py +126 -87
- wappa/cli/main.py +9 -4
- wappa/core/__init__.py +18 -23
- wappa/core/config/settings.py +7 -5
- wappa/core/events/default_handlers.py +1 -1
- wappa/core/factory/wappa_builder.py +38 -25
- wappa/core/plugins/redis_plugin.py +1 -3
- wappa/core/plugins/wappa_core_plugin.py +7 -6
- wappa/core/types.py +12 -12
- wappa/core/wappa_app.py +10 -8
- wappa/database/__init__.py +3 -4
- wappa/domain/enums/messenger_platform.py +1 -2
- wappa/domain/factories/media_factory.py +5 -20
- wappa/domain/factories/message_factory.py +5 -20
- wappa/domain/factories/messenger_factory.py +2 -4
- wappa/domain/interfaces/cache_interface.py +7 -7
- wappa/domain/interfaces/media_interface.py +2 -5
- wappa/domain/models/media_result.py +1 -3
- wappa/domain/models/platforms/platform_config.py +1 -3
- wappa/messaging/__init__.py +9 -12
- wappa/messaging/whatsapp/handlers/whatsapp_media_handler.py +20 -22
- wappa/models/__init__.py +27 -35
- wappa/persistence/__init__.py +12 -15
- wappa/persistence/cache_factory.py +0 -1
- wappa/persistence/json/__init__.py +1 -1
- wappa/persistence/json/cache_adapters.py +37 -25
- wappa/persistence/json/handlers/state_handler.py +60 -52
- wappa/persistence/json/handlers/table_handler.py +51 -49
- wappa/persistence/json/handlers/user_handler.py +71 -55
- wappa/persistence/json/handlers/utils/file_manager.py +42 -39
- wappa/persistence/json/handlers/utils/key_factory.py +1 -1
- wappa/persistence/json/handlers/utils/serialization.py +13 -11
- wappa/persistence/json/json_cache_factory.py +4 -8
- wappa/persistence/json/storage_manager.py +66 -79
- wappa/persistence/memory/__init__.py +1 -1
- wappa/persistence/memory/cache_adapters.py +37 -25
- wappa/persistence/memory/handlers/state_handler.py +62 -52
- wappa/persistence/memory/handlers/table_handler.py +59 -53
- wappa/persistence/memory/handlers/user_handler.py +75 -55
- wappa/persistence/memory/handlers/utils/key_factory.py +1 -1
- wappa/persistence/memory/handlers/utils/memory_store.py +75 -71
- wappa/persistence/memory/handlers/utils/ttl_manager.py +59 -67
- wappa/persistence/memory/memory_cache_factory.py +3 -7
- wappa/persistence/memory/storage_manager.py +52 -62
- wappa/persistence/redis/cache_adapters.py +27 -21
- wappa/persistence/redis/ops.py +11 -11
- wappa/persistence/redis/redis_client.py +4 -6
- wappa/persistence/redis/redis_manager.py +12 -4
- wappa/processors/factory.py +5 -5
- wappa/schemas/factory.py +2 -5
- wappa/schemas/whatsapp/message_types/errors.py +3 -12
- wappa/schemas/whatsapp/validators.py +3 -3
- wappa/webhooks/__init__.py +17 -18
- wappa/webhooks/factory.py +3 -5
- wappa/webhooks/whatsapp/__init__.py +10 -13
- wappa/webhooks/whatsapp/message_types/audio.py +0 -4
- wappa/webhooks/whatsapp/message_types/document.py +1 -9
- wappa/webhooks/whatsapp/message_types/errors.py +3 -12
- wappa/webhooks/whatsapp/message_types/location.py +1 -21
- wappa/webhooks/whatsapp/message_types/sticker.py +1 -5
- wappa/webhooks/whatsapp/message_types/text.py +0 -6
- wappa/webhooks/whatsapp/message_types/video.py +1 -20
- wappa/webhooks/whatsapp/status_models.py +2 -2
- wappa/webhooks/whatsapp/validators.py +3 -3
- {wappa-0.1.9.dist-info → wappa-0.1.10.dist-info}/METADATA +362 -8
- {wappa-0.1.9.dist-info → wappa-0.1.10.dist-info}/RECORD +126 -126
- {wappa-0.1.9.dist-info → wappa-0.1.10.dist-info}/WHEEL +0 -0
- {wappa-0.1.9.dist-info → wappa-0.1.10.dist-info}/entry_points.txt +0 -0
- {wappa-0.1.9.dist-info → wappa-0.1.10.dist-info}/licenses/LICENSE +0 -0
|
@@ -11,80 +11,82 @@ This module defines the main WappaEventHandler that:
|
|
|
11
11
|
- Follows Dependency Inversion with injected dependencies
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
import
|
|
15
|
-
from typing import Any, Dict
|
|
14
|
+
from typing import Any
|
|
16
15
|
|
|
17
|
-
from .scores import AVAILABLE_SCORES, ScoreBase, ScoreDependencies
|
|
18
|
-
from .scores.score_base import ScoreRegistry
|
|
19
|
-
from .utils.message_utils import extract_user_data, sanitize_message_text
|
|
20
16
|
from wappa import WappaEventHandler
|
|
21
17
|
from wappa.webhooks import ErrorWebhook, IncomingMessageWebhook, StatusWebhook
|
|
22
18
|
|
|
19
|
+
from .scores import AVAILABLE_SCORES, ScoreDependencies
|
|
20
|
+
from .scores.score_base import ScoreRegistry
|
|
21
|
+
from .utils.message_utils import extract_user_data, sanitize_message_text
|
|
22
|
+
|
|
23
23
|
|
|
24
24
|
class JSONCacheExampleHandler(WappaEventHandler):
|
|
25
25
|
"""
|
|
26
26
|
Main WappaEventHandler implementation for JSON cache example following SOLID principles.
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
This handler serves as the main entry point for the Wappa framework and demonstrates:
|
|
29
29
|
- Proper WappaEventHandler method implementations
|
|
30
30
|
- SOLID architecture with score module orchestration
|
|
31
31
|
- Dependency injection and lifecycle management
|
|
32
32
|
- Professional error handling and logging
|
|
33
33
|
"""
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
def __init__(self):
|
|
36
36
|
"""Initialize the JSON cache example handler."""
|
|
37
37
|
super().__init__()
|
|
38
|
-
|
|
38
|
+
|
|
39
39
|
# Score module registry (following Open/Closed Principle)
|
|
40
40
|
self.score_registry = ScoreRegistry()
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
# Processing statistics
|
|
43
43
|
self._total_messages = 0
|
|
44
44
|
self._successful_processing = 0
|
|
45
45
|
self._failed_processing = 0
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
# Master handler state
|
|
48
48
|
self._initialized = False
|
|
49
|
-
|
|
50
|
-
self.logger.info(
|
|
51
|
-
|
|
49
|
+
|
|
50
|
+
self.logger.info(
|
|
51
|
+
"🎯 JSONCacheExampleHandler initialized - ready for SOLID architecture setup"
|
|
52
|
+
)
|
|
53
|
+
|
|
52
54
|
async def process_message(self, webhook: IncomingMessageWebhook) -> None:
|
|
53
55
|
"""
|
|
54
56
|
Main message processing method required by WappaEventHandler.
|
|
55
|
-
|
|
57
|
+
|
|
56
58
|
This method orchestrates score modules following SOLID principles and
|
|
57
59
|
demonstrates proper webhook processing with dependency injection.
|
|
58
|
-
|
|
60
|
+
|
|
59
61
|
Args:
|
|
60
62
|
webhook: Incoming message webhook to process
|
|
61
63
|
"""
|
|
62
64
|
self._total_messages += 1
|
|
63
65
|
start_time = self._get_current_timestamp()
|
|
64
|
-
|
|
66
|
+
|
|
65
67
|
try:
|
|
66
68
|
# Initialize SOLID architecture on first message if not already done
|
|
67
69
|
if not self._initialized:
|
|
68
70
|
await self._initialize_solid_architecture()
|
|
69
|
-
|
|
71
|
+
|
|
70
72
|
# Extract basic user information for logging
|
|
71
73
|
user_data = extract_user_data(webhook)
|
|
72
|
-
user_id = user_data[
|
|
74
|
+
user_id = user_data["user_id"]
|
|
73
75
|
message_text = webhook.get_message_text() or "[NON-TEXT MESSAGE]"
|
|
74
|
-
|
|
76
|
+
|
|
75
77
|
self.logger.info(
|
|
76
78
|
f"📨 Processing message from {user_id}: "
|
|
77
79
|
f"{sanitize_message_text(message_text)[:50]}..."
|
|
78
80
|
)
|
|
79
|
-
|
|
81
|
+
|
|
80
82
|
# Execute score module processing pipeline
|
|
81
83
|
processing_result = await self._execute_score_pipeline(webhook)
|
|
82
|
-
|
|
84
|
+
|
|
83
85
|
# Record processing results
|
|
84
|
-
if processing_result[
|
|
86
|
+
if processing_result["success"]:
|
|
85
87
|
self._successful_processing += 1
|
|
86
88
|
processing_time = self._get_current_timestamp() - start_time
|
|
87
|
-
|
|
89
|
+
|
|
88
90
|
self.logger.info(
|
|
89
91
|
f"✅ Message processed successfully in {processing_time:.2f}s "
|
|
90
92
|
f"(processed by {processing_result['processed_count']} score modules)"
|
|
@@ -95,89 +97,99 @@ class JSONCacheExampleHandler(WappaEventHandler):
|
|
|
95
97
|
f"⚠️ Message processing completed with issues: "
|
|
96
98
|
f"{processing_result.get('error', 'Unknown error')}"
|
|
97
99
|
)
|
|
98
|
-
|
|
100
|
+
|
|
99
101
|
# Send fallback response to user
|
|
100
|
-
await self._send_error_response(
|
|
101
|
-
|
|
102
|
+
await self._send_error_response(
|
|
103
|
+
webhook, processing_result.get("error", "Processing error")
|
|
104
|
+
)
|
|
105
|
+
|
|
102
106
|
except Exception as e:
|
|
103
107
|
self._failed_processing += 1
|
|
104
|
-
self.logger.error(
|
|
108
|
+
self.logger.error(
|
|
109
|
+
f"❌ Critical error in message processing: {e}", exc_info=True
|
|
110
|
+
)
|
|
105
111
|
await self._send_error_response(webhook, f"System error: {str(e)}")
|
|
106
|
-
|
|
112
|
+
|
|
107
113
|
async def process_status(self, webhook: StatusWebhook) -> None:
|
|
108
114
|
"""
|
|
109
115
|
Process status webhooks from WhatsApp Business API.
|
|
110
|
-
|
|
116
|
+
|
|
111
117
|
Args:
|
|
112
118
|
webhook: Status webhook containing delivery status information
|
|
113
119
|
"""
|
|
114
120
|
try:
|
|
115
121
|
status_value = webhook.status.value
|
|
116
122
|
recipient = webhook.recipient_id
|
|
117
|
-
|
|
118
|
-
self.logger.info(
|
|
119
|
-
|
|
123
|
+
|
|
124
|
+
self.logger.info(
|
|
125
|
+
f"📊 Message status: {status_value.upper()} for {recipient}"
|
|
126
|
+
)
|
|
127
|
+
|
|
120
128
|
# You can add custom status processing logic here
|
|
121
129
|
# For example, updating delivery statistics or handling failed deliveries
|
|
122
|
-
|
|
130
|
+
|
|
123
131
|
except Exception as e:
|
|
124
132
|
self.logger.error(f"❌ Error processing status webhook: {e}", exc_info=True)
|
|
125
|
-
|
|
133
|
+
|
|
126
134
|
async def process_error(self, webhook: ErrorWebhook) -> None:
|
|
127
135
|
"""
|
|
128
136
|
Process error webhooks from WhatsApp Business API.
|
|
129
|
-
|
|
137
|
+
|
|
130
138
|
Args:
|
|
131
139
|
webhook: Error webhook containing error information
|
|
132
140
|
"""
|
|
133
141
|
try:
|
|
134
142
|
error_count = webhook.get_error_count()
|
|
135
143
|
primary_error = webhook.get_primary_error()
|
|
136
|
-
|
|
144
|
+
|
|
137
145
|
self.logger.error(
|
|
138
146
|
f"🚨 WhatsApp API error: {error_count} errors, "
|
|
139
147
|
f"primary: {primary_error.error_code} - {primary_error.error_title}"
|
|
140
148
|
)
|
|
141
|
-
|
|
149
|
+
|
|
142
150
|
# Record error in statistics
|
|
143
151
|
self._failed_processing += 1
|
|
144
|
-
|
|
152
|
+
|
|
145
153
|
# You can add custom error handling logic here
|
|
146
154
|
# For example, alerting systems or retry mechanisms
|
|
147
|
-
|
|
155
|
+
|
|
148
156
|
except Exception as e:
|
|
149
157
|
self.logger.error(f"❌ Error processing error webhook: {e}", exc_info=True)
|
|
150
|
-
|
|
158
|
+
|
|
151
159
|
async def _initialize_solid_architecture(self) -> None:
|
|
152
160
|
"""
|
|
153
161
|
Initialize SOLID architecture with score modules and dependency injection.
|
|
154
|
-
|
|
162
|
+
|
|
155
163
|
This method demonstrates Dependency Inversion Principle by injecting
|
|
156
164
|
abstractions and follows Single Responsibility Principle.
|
|
157
165
|
"""
|
|
158
166
|
try:
|
|
159
167
|
if not self.validate_dependencies():
|
|
160
|
-
self.logger.error(
|
|
168
|
+
self.logger.error(
|
|
169
|
+
"❌ Dependencies not properly injected - cannot initialize SOLID architecture"
|
|
170
|
+
)
|
|
161
171
|
return
|
|
162
|
-
|
|
172
|
+
|
|
163
173
|
if not self.cache_factory:
|
|
164
|
-
self.logger.error(
|
|
174
|
+
self.logger.error(
|
|
175
|
+
"❌ Cache factory not available - cannot initialize SOLID architecture"
|
|
176
|
+
)
|
|
165
177
|
return
|
|
166
|
-
|
|
178
|
+
|
|
167
179
|
# Create cache instances from factory (Dependency Inversion)
|
|
168
180
|
user_cache = self.cache_factory.create_user_cache()
|
|
169
181
|
table_cache = self.cache_factory.create_table_cache()
|
|
170
182
|
state_cache = self.cache_factory.create_state_cache()
|
|
171
|
-
|
|
183
|
+
|
|
172
184
|
# Create dependencies container
|
|
173
185
|
dependencies = ScoreDependencies(
|
|
174
|
-
messenger=self.messenger,
|
|
186
|
+
messenger=self.messenger,
|
|
175
187
|
user_cache=user_cache,
|
|
176
188
|
table_cache=table_cache,
|
|
177
189
|
state_cache=state_cache,
|
|
178
|
-
logger=self.logger
|
|
190
|
+
logger=self.logger,
|
|
179
191
|
)
|
|
180
|
-
|
|
192
|
+
|
|
181
193
|
# Auto-register all available score modules (Open/Closed Principle)
|
|
182
194
|
registered_count = 0
|
|
183
195
|
for score_class in AVAILABLE_SCORES:
|
|
@@ -186,142 +198,162 @@ class JSONCacheExampleHandler(WappaEventHandler):
|
|
|
186
198
|
score_instance = score_class(dependencies)
|
|
187
199
|
self.score_registry.register_score(score_instance)
|
|
188
200
|
registered_count += 1
|
|
189
|
-
|
|
201
|
+
|
|
190
202
|
self.logger.info(
|
|
191
203
|
f"✅ Registered score module: {score_instance.score_name}"
|
|
192
204
|
)
|
|
193
|
-
|
|
205
|
+
|
|
194
206
|
except Exception as e:
|
|
195
207
|
self.logger.error(
|
|
196
208
|
f"❌ Failed to register {score_class.__name__}: {e}"
|
|
197
209
|
)
|
|
198
|
-
|
|
210
|
+
|
|
199
211
|
self._initialized = True
|
|
200
212
|
self.logger.info(
|
|
201
213
|
f"🎯 SOLID architecture initialized successfully: {registered_count} score modules registered"
|
|
202
214
|
)
|
|
203
|
-
|
|
215
|
+
|
|
204
216
|
except Exception as e:
|
|
205
|
-
self.logger.error(
|
|
217
|
+
self.logger.error(
|
|
218
|
+
f"❌ Critical error initializing SOLID architecture: {e}", exc_info=True
|
|
219
|
+
)
|
|
206
220
|
raise
|
|
207
|
-
|
|
208
|
-
async def _execute_score_pipeline(
|
|
221
|
+
|
|
222
|
+
async def _execute_score_pipeline(
|
|
223
|
+
self, webhook: IncomingMessageWebhook
|
|
224
|
+
) -> dict[str, Any]:
|
|
209
225
|
"""
|
|
210
226
|
Execute the score module processing pipeline.
|
|
211
|
-
|
|
227
|
+
|
|
212
228
|
Processes webhook through all applicable score modules following
|
|
213
229
|
the Chain of Responsibility pattern.
|
|
214
|
-
|
|
230
|
+
|
|
215
231
|
Args:
|
|
216
232
|
webhook: Webhook to process
|
|
217
|
-
|
|
233
|
+
|
|
218
234
|
Returns:
|
|
219
235
|
Processing result with success status and metadata
|
|
220
236
|
"""
|
|
221
237
|
try:
|
|
222
238
|
if not self._initialized:
|
|
223
239
|
return {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
240
|
+
"success": False,
|
|
241
|
+
"error": "SOLID architecture not initialized",
|
|
242
|
+
"processed_count": 0,
|
|
227
243
|
}
|
|
228
|
-
|
|
244
|
+
|
|
229
245
|
scores = self.score_registry.get_scores()
|
|
230
246
|
processed_count = 0
|
|
231
247
|
processing_errors = []
|
|
232
|
-
|
|
248
|
+
|
|
233
249
|
# Process webhook through all applicable score modules
|
|
234
250
|
for score in scores:
|
|
235
251
|
try:
|
|
236
252
|
# Check if score can handle this webhook (Interface Segregation)
|
|
237
253
|
can_handle = await score.can_handle(webhook)
|
|
238
|
-
|
|
254
|
+
|
|
239
255
|
if can_handle:
|
|
240
256
|
self.logger.debug(f"🎯 Processing with {score.score_name}")
|
|
241
|
-
|
|
257
|
+
|
|
242
258
|
# Process with the score module
|
|
243
259
|
success = await score.process(webhook)
|
|
244
|
-
|
|
260
|
+
|
|
245
261
|
if success:
|
|
246
262
|
processed_count += 1
|
|
247
|
-
self.logger.debug(
|
|
263
|
+
self.logger.debug(
|
|
264
|
+
f"✅ {score.score_name} completed successfully"
|
|
265
|
+
)
|
|
248
266
|
else:
|
|
249
|
-
processing_errors.append(
|
|
250
|
-
|
|
267
|
+
processing_errors.append(
|
|
268
|
+
f"{score.score_name}: Processing failed"
|
|
269
|
+
)
|
|
270
|
+
self.logger.warning(
|
|
271
|
+
f"⚠️ {score.score_name} reported processing failure"
|
|
272
|
+
)
|
|
251
273
|
else:
|
|
252
|
-
self.logger.debug(
|
|
253
|
-
|
|
274
|
+
self.logger.debug(
|
|
275
|
+
f"⏭️ {score.score_name} skipped (cannot handle this webhook)"
|
|
276
|
+
)
|
|
277
|
+
|
|
254
278
|
except Exception as score_error:
|
|
255
279
|
processing_errors.append(f"{score.score_name}: {str(score_error)}")
|
|
256
280
|
self.logger.error(
|
|
257
|
-
f"❌ Error in {score.score_name}: {score_error}",
|
|
258
|
-
exc_info=True
|
|
281
|
+
f"❌ Error in {score.score_name}: {score_error}", exc_info=True
|
|
259
282
|
)
|
|
260
|
-
|
|
283
|
+
|
|
261
284
|
# Determine overall success
|
|
262
285
|
overall_success = processed_count > 0 and len(processing_errors) == 0
|
|
263
|
-
|
|
286
|
+
|
|
264
287
|
return {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
288
|
+
"success": overall_success,
|
|
289
|
+
"processed_count": processed_count,
|
|
290
|
+
"total_scores": len(scores),
|
|
291
|
+
"errors": processing_errors if processing_errors else None,
|
|
292
|
+
"message": (
|
|
270
293
|
f"Processed by {processed_count}/{len(scores)} score modules"
|
|
271
|
-
+ (
|
|
272
|
-
|
|
294
|
+
+ (
|
|
295
|
+
f" with {len(processing_errors)} errors"
|
|
296
|
+
if processing_errors
|
|
297
|
+
else ""
|
|
298
|
+
)
|
|
299
|
+
),
|
|
273
300
|
}
|
|
274
|
-
|
|
301
|
+
|
|
275
302
|
except Exception as e:
|
|
276
|
-
self.logger.error(
|
|
303
|
+
self.logger.error(
|
|
304
|
+
f"❌ Critical error in score pipeline: {e}", exc_info=True
|
|
305
|
+
)
|
|
277
306
|
return {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
307
|
+
"success": False,
|
|
308
|
+
"processed_count": 0,
|
|
309
|
+
"error": f"Pipeline error: {str(e)}",
|
|
281
310
|
}
|
|
282
|
-
|
|
283
|
-
async def _send_error_response(
|
|
311
|
+
|
|
312
|
+
async def _send_error_response(
|
|
313
|
+
self, webhook: IncomingMessageWebhook, error_details: str
|
|
314
|
+
) -> None:
|
|
284
315
|
"""
|
|
285
316
|
Send user-friendly error response when processing fails.
|
|
286
|
-
|
|
317
|
+
|
|
287
318
|
Args:
|
|
288
319
|
webhook: Original webhook that failed to process
|
|
289
320
|
error_details: Details about the error for logging
|
|
290
321
|
"""
|
|
291
322
|
try:
|
|
292
323
|
user_data = extract_user_data(webhook)
|
|
293
|
-
user_id = user_data[
|
|
294
|
-
|
|
324
|
+
user_id = user_data["user_id"]
|
|
325
|
+
|
|
295
326
|
error_message = (
|
|
296
327
|
"🚨 SOLID JSON Cache Example\n\n"
|
|
297
328
|
"❌ An error occurred while processing your message.\n"
|
|
298
329
|
"Our team has been notified and will resolve this issue soon.\n\n"
|
|
299
330
|
"Please try again later or contact support if the problem persists."
|
|
300
331
|
)
|
|
301
|
-
|
|
332
|
+
|
|
302
333
|
result = await self.messenger.send_text(
|
|
303
334
|
recipient=user_id,
|
|
304
335
|
text=error_message,
|
|
305
|
-
reply_to_message_id=webhook.message.message_id
|
|
336
|
+
reply_to_message_id=webhook.message.message_id,
|
|
306
337
|
)
|
|
307
|
-
|
|
338
|
+
|
|
308
339
|
if result.success:
|
|
309
340
|
self.logger.info(f"🚨 Error response sent to {user_id}")
|
|
310
341
|
else:
|
|
311
342
|
self.logger.error(f"❌ Failed to send error response: {result.error}")
|
|
312
|
-
|
|
343
|
+
|
|
313
344
|
except Exception as e:
|
|
314
345
|
self.logger.error(f"❌ Error sending error response: {e}")
|
|
315
|
-
|
|
346
|
+
|
|
316
347
|
def _get_current_timestamp(self) -> float:
|
|
317
348
|
"""Get current timestamp for performance measurement."""
|
|
318
349
|
import time
|
|
350
|
+
|
|
319
351
|
return time.time()
|
|
320
|
-
|
|
321
|
-
async def get_handler_statistics(self) ->
|
|
352
|
+
|
|
353
|
+
async def get_handler_statistics(self) -> dict[str, Any]:
|
|
322
354
|
"""
|
|
323
355
|
Get comprehensive handler and score module statistics.
|
|
324
|
-
|
|
356
|
+
|
|
325
357
|
Returns:
|
|
326
358
|
Dictionary with processing statistics and score module metrics
|
|
327
359
|
"""
|
|
@@ -329,58 +361,63 @@ class JSONCacheExampleHandler(WappaEventHandler):
|
|
|
329
361
|
# Calculate success rate
|
|
330
362
|
success_rate = (
|
|
331
363
|
(self._successful_processing / self._total_messages)
|
|
332
|
-
if self._total_messages > 0
|
|
364
|
+
if self._total_messages > 0
|
|
365
|
+
else 0.0
|
|
333
366
|
)
|
|
334
|
-
|
|
367
|
+
|
|
335
368
|
# Get score-specific statistics if initialized
|
|
336
369
|
score_stats = {}
|
|
337
370
|
if self._initialized:
|
|
338
371
|
score_stats = self.score_registry.get_score_stats()
|
|
339
|
-
|
|
372
|
+
|
|
340
373
|
return {
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
374
|
+
"handler_status": "initialized"
|
|
375
|
+
if self._initialized
|
|
376
|
+
else "pending_initialization",
|
|
377
|
+
"total_messages": self._total_messages,
|
|
378
|
+
"successful_processing": self._successful_processing,
|
|
379
|
+
"failed_processing": self._failed_processing,
|
|
380
|
+
"success_rate": success_rate,
|
|
381
|
+
"registered_scores": len(self.score_registry.get_scores())
|
|
382
|
+
if self._initialized
|
|
383
|
+
else 0,
|
|
384
|
+
"score_modules": score_stats,
|
|
348
385
|
}
|
|
349
|
-
|
|
386
|
+
|
|
350
387
|
except Exception as e:
|
|
351
388
|
self.logger.error(f"❌ Error collecting handler statistics: {e}")
|
|
352
|
-
return {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
async def validate_system_health(self) -> Dict[str, Any]:
|
|
389
|
+
return {"error": f"Statistics collection failed: {str(e)}"}
|
|
390
|
+
|
|
391
|
+
async def validate_system_health(self) -> dict[str, Any]:
|
|
357
392
|
"""
|
|
358
393
|
Validate system health including all score modules and dependencies.
|
|
359
|
-
|
|
394
|
+
|
|
360
395
|
Returns:
|
|
361
396
|
Health check results for the entire system
|
|
362
397
|
"""
|
|
363
398
|
try:
|
|
364
399
|
health_results = {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
400
|
+
"overall_healthy": True,
|
|
401
|
+
"initialized": self._initialized,
|
|
402
|
+
"components": {},
|
|
403
|
+
"registered_scores": len(self.score_registry.get_scores())
|
|
404
|
+
if self._initialized
|
|
405
|
+
else 0,
|
|
369
406
|
}
|
|
370
|
-
|
|
407
|
+
|
|
371
408
|
# Check core dependencies
|
|
372
409
|
core_components = {
|
|
373
|
-
|
|
374
|
-
|
|
410
|
+
"messenger": self.messenger,
|
|
411
|
+
"cache_factory": self.cache_factory,
|
|
375
412
|
}
|
|
376
|
-
|
|
413
|
+
|
|
377
414
|
for component_name, component in core_components.items():
|
|
378
415
|
if component is not None:
|
|
379
|
-
health_results[
|
|
416
|
+
health_results["components"][component_name] = "Available"
|
|
380
417
|
else:
|
|
381
|
-
health_results[
|
|
382
|
-
health_results[
|
|
383
|
-
|
|
418
|
+
health_results["components"][component_name] = "Missing"
|
|
419
|
+
health_results["overall_healthy"] = False
|
|
420
|
+
|
|
384
421
|
# Check score modules if initialized
|
|
385
422
|
if self._initialized:
|
|
386
423
|
scores = self.score_registry.get_scores()
|
|
@@ -388,32 +425,31 @@ class JSONCacheExampleHandler(WappaEventHandler):
|
|
|
388
425
|
try:
|
|
389
426
|
# Basic validation check
|
|
390
427
|
is_valid = await score.validate_dependencies()
|
|
391
|
-
health_results[
|
|
392
|
-
|
|
428
|
+
health_results["components"][score.score_name] = (
|
|
429
|
+
"Healthy" if is_valid else "Dependency Issues"
|
|
393
430
|
)
|
|
394
431
|
if not is_valid:
|
|
395
|
-
health_results[
|
|
396
|
-
|
|
432
|
+
health_results["overall_healthy"] = False
|
|
433
|
+
|
|
397
434
|
except Exception as e:
|
|
398
|
-
health_results[
|
|
399
|
-
|
|
400
|
-
|
|
435
|
+
health_results["components"][score.score_name] = (
|
|
436
|
+
f"Error: {str(e)}"
|
|
437
|
+
)
|
|
438
|
+
health_results["overall_healthy"] = False
|
|
439
|
+
|
|
401
440
|
return health_results
|
|
402
|
-
|
|
441
|
+
|
|
403
442
|
except Exception as e:
|
|
404
443
|
self.logger.error(f"❌ Error validating system health: {e}")
|
|
405
|
-
return {
|
|
406
|
-
|
|
407
|
-
'error': f"Health check failed: {str(e)}"
|
|
408
|
-
}
|
|
409
|
-
|
|
444
|
+
return {"overall_healthy": False, "error": f"Health check failed: {str(e)}"}
|
|
445
|
+
|
|
410
446
|
def __str__(self) -> str:
|
|
411
447
|
"""String representation of the handler."""
|
|
412
448
|
return (
|
|
413
449
|
f"JSONCacheExampleHandler("
|
|
414
450
|
f"messages={self._total_messages}, "
|
|
415
|
-
f"success_rate={self._successful_processing/max(1, self._total_messages):.2%}, "
|
|
451
|
+
f"success_rate={self._successful_processing / max(1, self._total_messages):.2%}, "
|
|
416
452
|
f"scores={len(self.score_registry.get_scores()) if self._initialized else 'pending'}, "
|
|
417
453
|
f"initialized={self._initialized}"
|
|
418
454
|
f")"
|
|
419
|
-
)
|
|
455
|
+
)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"""JSON cache example models package."""
|
|
1
|
+
"""JSON cache example models package."""
|