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
|
@@ -7,7 +7,6 @@ commands, state management, and all message type handling.
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
import time
|
|
10
|
-
from typing import Dict, Optional
|
|
11
10
|
|
|
12
11
|
from wappa import WappaEventHandler
|
|
13
12
|
from wappa.webhooks import ErrorWebhook, IncomingMessageWebhook, StatusWebhook
|
|
@@ -19,7 +18,6 @@ from .handlers.command_handlers import (
|
|
|
19
18
|
)
|
|
20
19
|
from .handlers.message_handlers import MessageHandlers, handle_message_by_type
|
|
21
20
|
from .handlers.state_handlers import StateHandlers, handle_user_in_state
|
|
22
|
-
from .models.state_models import StateType
|
|
23
21
|
from .models.user_models import UserProfile
|
|
24
22
|
from .utils.cache_utils import CacheHelper
|
|
25
23
|
|
|
@@ -27,7 +25,7 @@ from .utils.cache_utils import CacheHelper
|
|
|
27
25
|
class WappaFullExampleHandler(WappaEventHandler):
|
|
28
26
|
"""
|
|
29
27
|
Comprehensive WappaEventHandler implementation demonstrating all framework features.
|
|
30
|
-
|
|
28
|
+
|
|
31
29
|
Features:
|
|
32
30
|
- Complete message type handling with metadata extraction
|
|
33
31
|
- Interactive commands (/button, /list, /cta, /location) with state management
|
|
@@ -37,17 +35,17 @@ class WappaFullExampleHandler(WappaEventHandler):
|
|
|
37
35
|
- Welcome messages for first-time users
|
|
38
36
|
- State-based interactive workflows with TTL
|
|
39
37
|
"""
|
|
40
|
-
|
|
38
|
+
|
|
41
39
|
def __init__(self):
|
|
42
40
|
"""Initialize the comprehensive Wappa example handler."""
|
|
43
41
|
super().__init__()
|
|
44
|
-
|
|
42
|
+
|
|
45
43
|
# Handler instances (initialized per request)
|
|
46
|
-
self.cache_helper:
|
|
47
|
-
self.message_handlers:
|
|
48
|
-
self.command_handlers:
|
|
49
|
-
self.state_handlers:
|
|
50
|
-
|
|
44
|
+
self.cache_helper: CacheHelper | None = None
|
|
45
|
+
self.message_handlers: MessageHandlers | None = None
|
|
46
|
+
self.command_handlers: CommandHandlers | None = None
|
|
47
|
+
self.state_handlers: StateHandlers | None = None
|
|
48
|
+
|
|
51
49
|
# Statistics
|
|
52
50
|
self._total_messages = 0
|
|
53
51
|
self._successful_processing = 0
|
|
@@ -55,13 +53,15 @@ class WappaFullExampleHandler(WappaEventHandler):
|
|
|
55
53
|
self._first_time_users = 0
|
|
56
54
|
self._commands_processed = 0
|
|
57
55
|
self._interactive_responses = 0
|
|
58
|
-
|
|
59
|
-
self.logger.info(
|
|
60
|
-
|
|
56
|
+
|
|
57
|
+
self.logger.info(
|
|
58
|
+
"🚀 WappaFullExampleHandler initialized - comprehensive demo ready"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
61
|
async def process_message(self, webhook: IncomingMessageWebhook) -> None:
|
|
62
62
|
"""
|
|
63
63
|
Main message processing method with comprehensive functionality.
|
|
64
|
-
|
|
64
|
+
|
|
65
65
|
This method orchestrates:
|
|
66
66
|
1. Dependency validation and setup
|
|
67
67
|
2. User profile management and welcome messages
|
|
@@ -70,55 +70,61 @@ class WappaFullExampleHandler(WappaEventHandler):
|
|
|
70
70
|
5. Special command handling
|
|
71
71
|
6. Regular message processing with metadata extraction
|
|
72
72
|
7. Statistics tracking and logging
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
Args:
|
|
75
75
|
webhook: IncomingMessageWebhook containing message data
|
|
76
76
|
"""
|
|
77
77
|
start_time = time.time()
|
|
78
78
|
self._total_messages += 1
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
try:
|
|
81
81
|
# 1. Setup dependencies and validate
|
|
82
82
|
if not await self._setup_dependencies():
|
|
83
83
|
self._failed_processing += 1
|
|
84
84
|
return
|
|
85
|
-
|
|
85
|
+
|
|
86
86
|
# 2. Handle user profile and first-time user welcome
|
|
87
87
|
user_profile = await self._handle_user_profile_and_welcome(webhook)
|
|
88
88
|
if not user_profile:
|
|
89
|
-
self.logger.warning(
|
|
89
|
+
self.logger.warning(
|
|
90
|
+
"Failed to handle user profile, continuing without caching"
|
|
91
|
+
)
|
|
90
92
|
user_profile = UserProfile(phone_number=webhook.user.user_id)
|
|
91
|
-
|
|
93
|
+
|
|
92
94
|
# 3. Mark message as read (as specified in requirements)
|
|
93
95
|
await self._mark_message_as_read(webhook)
|
|
94
|
-
|
|
96
|
+
|
|
95
97
|
# Extract basic message info for routing
|
|
96
98
|
user_id = webhook.user.user_id
|
|
97
99
|
message_text = webhook.get_message_text().strip()
|
|
98
100
|
message_type = webhook.get_message_type_name()
|
|
99
|
-
|
|
101
|
+
|
|
100
102
|
self.logger.info(
|
|
101
103
|
f"📨 Processing {message_type} from {user_id}: "
|
|
102
104
|
f"'{message_text[:50]}{'...' if len(message_text) > 50 else ''}'"
|
|
103
105
|
)
|
|
104
|
-
|
|
106
|
+
|
|
105
107
|
# 4. Check for active interactive states first
|
|
106
108
|
state_result = await handle_user_in_state(
|
|
107
109
|
webhook, user_profile, self.messenger, self.cache_factory, self.logger
|
|
108
110
|
)
|
|
109
|
-
|
|
111
|
+
|
|
110
112
|
if state_result is not None:
|
|
111
113
|
# User was in an interactive state - handle accordingly
|
|
112
114
|
if state_result["success"]:
|
|
113
115
|
self._interactive_responses += 1
|
|
114
116
|
self._successful_processing += 1
|
|
115
|
-
self.logger.info(
|
|
117
|
+
self.logger.info(
|
|
118
|
+
f"✅ Interactive state handled: {state_result.get('selection_type', 'unknown')}"
|
|
119
|
+
)
|
|
116
120
|
else:
|
|
117
|
-
self.logger.info(
|
|
118
|
-
|
|
121
|
+
self.logger.info(
|
|
122
|
+
f"🔄 Interactive state reminder sent: {state_result.get('error', 'unknown')}"
|
|
123
|
+
)
|
|
124
|
+
|
|
119
125
|
await self._log_processing_stats(start_time)
|
|
120
126
|
return
|
|
121
|
-
|
|
127
|
+
|
|
122
128
|
# 5. Check for special commands (only for text messages)
|
|
123
129
|
if message_type == "text" and is_special_command(message_text):
|
|
124
130
|
command = get_command_from_text(message_text)
|
|
@@ -127,42 +133,48 @@ class WappaFullExampleHandler(WappaEventHandler):
|
|
|
127
133
|
self._successful_processing += 1
|
|
128
134
|
await self._log_processing_stats(start_time)
|
|
129
135
|
return
|
|
130
|
-
|
|
136
|
+
|
|
131
137
|
# 6. Handle regular message processing with metadata
|
|
132
138
|
result = await handle_message_by_type(
|
|
133
139
|
webhook, user_profile, self.messenger, self.cache_factory, self.logger
|
|
134
140
|
)
|
|
135
|
-
|
|
141
|
+
|
|
136
142
|
if result["success"]:
|
|
137
143
|
self._successful_processing += 1
|
|
138
144
|
self.logger.info(f"✅ {message_type} message processed successfully")
|
|
139
145
|
else:
|
|
140
146
|
self._failed_processing += 1
|
|
141
|
-
self.logger.error(
|
|
142
|
-
|
|
147
|
+
self.logger.error(
|
|
148
|
+
f"❌ {message_type} message processing failed: {result.get('error')}"
|
|
149
|
+
)
|
|
150
|
+
|
|
143
151
|
# Send error response to user
|
|
144
|
-
await self._send_error_response(
|
|
145
|
-
|
|
152
|
+
await self._send_error_response(
|
|
153
|
+
webhook, result.get("error", "Processing failed")
|
|
154
|
+
)
|
|
155
|
+
|
|
146
156
|
await self._log_processing_stats(start_time)
|
|
147
|
-
|
|
157
|
+
|
|
148
158
|
except Exception as e:
|
|
149
159
|
self._failed_processing += 1
|
|
150
|
-
self.logger.error(
|
|
151
|
-
|
|
160
|
+
self.logger.error(
|
|
161
|
+
f"❌ Critical error in message processing: {e}", exc_info=True
|
|
162
|
+
)
|
|
163
|
+
|
|
152
164
|
# Send generic error response
|
|
153
165
|
try:
|
|
154
166
|
await self.messenger.send_text(
|
|
155
167
|
recipient=webhook.user.user_id,
|
|
156
168
|
text="❌ Sorry, something went wrong processing your message. Please try again.",
|
|
157
|
-
reply_to_message_id=webhook.message.message_id
|
|
169
|
+
reply_to_message_id=webhook.message.message_id,
|
|
158
170
|
)
|
|
159
171
|
except Exception as send_error:
|
|
160
172
|
self.logger.error(f"❌ Failed to send error response: {send_error}")
|
|
161
|
-
|
|
173
|
+
|
|
162
174
|
async def process_status(self, webhook: StatusWebhook) -> None:
|
|
163
175
|
"""
|
|
164
176
|
Process status webhooks from WhatsApp Business API.
|
|
165
|
-
|
|
177
|
+
|
|
166
178
|
Args:
|
|
167
179
|
webhook: StatusWebhook containing delivery status information
|
|
168
180
|
"""
|
|
@@ -170,93 +182,110 @@ class WappaFullExampleHandler(WappaEventHandler):
|
|
|
170
182
|
status_value = webhook.status.value
|
|
171
183
|
recipient = webhook.recipient_id
|
|
172
184
|
message_id = webhook.message_id
|
|
173
|
-
|
|
174
|
-
self.logger.info(
|
|
175
|
-
|
|
185
|
+
|
|
186
|
+
self.logger.info(
|
|
187
|
+
f"📊 Message status: {status_value.upper()} for {recipient} (msg: {message_id[:20]}...)"
|
|
188
|
+
)
|
|
189
|
+
|
|
176
190
|
# You can add custom status processing here
|
|
177
191
|
# For example: update delivery statistics, handle failed deliveries, etc.
|
|
178
|
-
|
|
192
|
+
|
|
179
193
|
except Exception as e:
|
|
180
194
|
self.logger.error(f"❌ Error processing status webhook: {e}", exc_info=True)
|
|
181
|
-
|
|
195
|
+
|
|
182
196
|
async def process_error(self, webhook: ErrorWebhook) -> None:
|
|
183
197
|
"""
|
|
184
198
|
Process error webhooks from WhatsApp Business API.
|
|
185
|
-
|
|
199
|
+
|
|
186
200
|
Args:
|
|
187
201
|
webhook: ErrorWebhook containing error information
|
|
188
202
|
"""
|
|
189
203
|
try:
|
|
190
204
|
error_count = webhook.get_error_count()
|
|
191
205
|
primary_error = webhook.get_primary_error()
|
|
192
|
-
|
|
206
|
+
|
|
193
207
|
self.logger.error(
|
|
194
208
|
f"🚨 WhatsApp API error: {error_count} errors, "
|
|
195
209
|
f"primary: {primary_error.error_code} - {primary_error.error_title}"
|
|
196
210
|
)
|
|
197
|
-
|
|
211
|
+
|
|
198
212
|
# Record error in statistics
|
|
199
213
|
self._failed_processing += 1
|
|
200
|
-
|
|
214
|
+
|
|
201
215
|
# You can add custom error handling logic here
|
|
202
216
|
# For example: alerting systems, retry mechanisms, etc.
|
|
203
|
-
|
|
217
|
+
|
|
204
218
|
except Exception as e:
|
|
205
219
|
self.logger.error(f"❌ Error processing error webhook: {e}", exc_info=True)
|
|
206
|
-
|
|
220
|
+
|
|
207
221
|
async def _setup_dependencies(self) -> bool:
|
|
208
222
|
"""Setup handler dependencies and validate state."""
|
|
209
223
|
if not self.validate_dependencies():
|
|
210
224
|
self.logger.error("❌ Dependencies not properly injected")
|
|
211
225
|
return False
|
|
212
|
-
|
|
226
|
+
|
|
213
227
|
if not self.cache_factory:
|
|
214
|
-
self.logger.error(
|
|
228
|
+
self.logger.error(
|
|
229
|
+
"❌ Cache factory not available - Redis caching unavailable"
|
|
230
|
+
)
|
|
215
231
|
return False
|
|
216
|
-
|
|
232
|
+
|
|
217
233
|
try:
|
|
218
234
|
# Initialize helper instances
|
|
219
235
|
self.cache_helper = CacheHelper(self.cache_factory)
|
|
220
|
-
self.message_handlers = MessageHandlers(
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
236
|
+
self.message_handlers = MessageHandlers(
|
|
237
|
+
self.messenger, self.cache_factory, self.logger
|
|
238
|
+
)
|
|
239
|
+
self.command_handlers = CommandHandlers(
|
|
240
|
+
self.messenger, self.cache_factory, self.logger
|
|
241
|
+
)
|
|
242
|
+
self.state_handlers = StateHandlers(
|
|
243
|
+
self.messenger, self.cache_factory, self.logger
|
|
244
|
+
)
|
|
245
|
+
|
|
224
246
|
self.logger.debug("✅ Handler dependencies initialized successfully")
|
|
225
247
|
return True
|
|
226
|
-
|
|
248
|
+
|
|
227
249
|
except Exception as e:
|
|
228
250
|
self.logger.error(f"❌ Failed to setup dependencies: {e}", exc_info=True)
|
|
229
251
|
return False
|
|
230
|
-
|
|
231
|
-
async def _handle_user_profile_and_welcome(
|
|
252
|
+
|
|
253
|
+
async def _handle_user_profile_and_welcome(
|
|
254
|
+
self, webhook: IncomingMessageWebhook
|
|
255
|
+
) -> UserProfile | None:
|
|
232
256
|
"""Handle user profile caching and send welcome message to first-time users."""
|
|
233
257
|
try:
|
|
234
258
|
user_id = webhook.user.user_id
|
|
235
259
|
user_name = webhook.user.profile_name
|
|
236
|
-
|
|
260
|
+
|
|
237
261
|
# Get or create user profile
|
|
238
262
|
user_profile = await self.cache_helper.get_or_create_user_profile(
|
|
239
263
|
user_id, user_name, user_name
|
|
240
264
|
)
|
|
241
|
-
|
|
265
|
+
|
|
242
266
|
# Send welcome message to first-time users
|
|
243
|
-
if
|
|
267
|
+
if (
|
|
268
|
+
user_profile.is_first_time_user
|
|
269
|
+
and not user_profile.has_received_welcome
|
|
270
|
+
):
|
|
244
271
|
await self._send_welcome_message(webhook, user_profile)
|
|
245
272
|
user_profile.mark_welcome_sent()
|
|
246
273
|
await self.cache_helper.save_user_profile(user_profile)
|
|
247
274
|
self._first_time_users += 1
|
|
248
|
-
|
|
275
|
+
|
|
249
276
|
return user_profile
|
|
250
|
-
|
|
277
|
+
|
|
251
278
|
except Exception as e:
|
|
252
279
|
self.logger.error(f"❌ Error handling user profile: {e}", exc_info=True)
|
|
253
280
|
return None
|
|
254
|
-
|
|
255
|
-
async def _send_welcome_message(
|
|
281
|
+
|
|
282
|
+
async def _send_welcome_message(
|
|
283
|
+
self, webhook: IncomingMessageWebhook, user_profile: UserProfile
|
|
284
|
+
) -> None:
|
|
256
285
|
"""Send welcome message with instructions to first-time users."""
|
|
257
286
|
user_id = webhook.user.user_id
|
|
258
287
|
display_name = user_profile.get_display_name()
|
|
259
|
-
|
|
288
|
+
|
|
260
289
|
welcome_text = (
|
|
261
290
|
f"🎉 *Welcome to Wappa Full Example, {display_name}!*\n\n"
|
|
262
291
|
f"This is a comprehensive demonstration of the Wappa framework capabilities.\n\n"
|
|
@@ -278,68 +307,86 @@ class WappaFullExampleHandler(WappaEventHandler):
|
|
|
278
307
|
f"• Contacts → Contact information echoed back\n\n"
|
|
279
308
|
f"💡 *Pro tip*: This demo showcases production-ready patterns for building WhatsApp Business applications!"
|
|
280
309
|
)
|
|
281
|
-
|
|
310
|
+
|
|
282
311
|
try:
|
|
283
312
|
result = await self.messenger.send_text(
|
|
284
313
|
recipient=user_id,
|
|
285
314
|
text=welcome_text,
|
|
286
|
-
reply_to_message_id=webhook.message.message_id
|
|
315
|
+
reply_to_message_id=webhook.message.message_id,
|
|
287
316
|
)
|
|
288
|
-
|
|
317
|
+
|
|
289
318
|
if result.success:
|
|
290
|
-
self.logger.info(
|
|
319
|
+
self.logger.info(
|
|
320
|
+
f"👋 Welcome message sent to {display_name} ({user_id})"
|
|
321
|
+
)
|
|
291
322
|
else:
|
|
292
323
|
self.logger.error(f"❌ Failed to send welcome message: {result.error}")
|
|
293
|
-
|
|
324
|
+
|
|
294
325
|
except Exception as e:
|
|
295
326
|
self.logger.error(f"❌ Error sending welcome message: {e}")
|
|
296
|
-
|
|
327
|
+
|
|
297
328
|
async def _mark_message_as_read(self, webhook: IncomingMessageWebhook) -> None:
|
|
298
329
|
"""Mark incoming message as read (as specified in requirements)."""
|
|
299
330
|
try:
|
|
300
331
|
result = await self.messenger.mark_as_read(
|
|
301
|
-
message_id=webhook.message.message_id,
|
|
302
|
-
typing=False
|
|
332
|
+
message_id=webhook.message.message_id, typing=False
|
|
303
333
|
)
|
|
304
|
-
|
|
334
|
+
|
|
305
335
|
if result.success:
|
|
306
|
-
self.logger.debug(
|
|
336
|
+
self.logger.debug(
|
|
337
|
+
f"✅ Message marked as read: {webhook.message.message_id[:20]}..."
|
|
338
|
+
)
|
|
307
339
|
else:
|
|
308
340
|
self.logger.warning(f"⚠️ Failed to mark message as read: {result.error}")
|
|
309
|
-
|
|
341
|
+
|
|
310
342
|
except Exception as e:
|
|
311
343
|
self.logger.warning(f"⚠️ Error marking message as read: {e}")
|
|
312
|
-
|
|
313
|
-
async def _handle_special_command(
|
|
314
|
-
|
|
344
|
+
|
|
345
|
+
async def _handle_special_command(
|
|
346
|
+
self, webhook: IncomingMessageWebhook, user_profile: UserProfile, command: str
|
|
347
|
+
) -> None:
|
|
315
348
|
"""Handle special commands using command handlers."""
|
|
316
349
|
try:
|
|
317
350
|
if command == "/button":
|
|
318
|
-
result = await self.command_handlers.handle_button_command(
|
|
351
|
+
result = await self.command_handlers.handle_button_command(
|
|
352
|
+
webhook, user_profile
|
|
353
|
+
)
|
|
319
354
|
elif command == "/list":
|
|
320
|
-
result = await self.command_handlers.handle_list_command(
|
|
355
|
+
result = await self.command_handlers.handle_list_command(
|
|
356
|
+
webhook, user_profile
|
|
357
|
+
)
|
|
321
358
|
elif command == "/cta":
|
|
322
|
-
result = await self.command_handlers.handle_cta_command(
|
|
359
|
+
result = await self.command_handlers.handle_cta_command(
|
|
360
|
+
webhook, user_profile
|
|
361
|
+
)
|
|
323
362
|
elif command == "/location":
|
|
324
|
-
result = await self.command_handlers.handle_location_command(
|
|
363
|
+
result = await self.command_handlers.handle_location_command(
|
|
364
|
+
webhook, user_profile
|
|
365
|
+
)
|
|
325
366
|
else:
|
|
326
367
|
self.logger.warning(f"Unsupported command: {command}")
|
|
327
368
|
return
|
|
328
|
-
|
|
369
|
+
|
|
329
370
|
if result["success"]:
|
|
330
371
|
self.logger.info(f"✅ Command {command} processed successfully")
|
|
331
372
|
else:
|
|
332
|
-
self.logger.error(
|
|
333
|
-
|
|
373
|
+
self.logger.error(
|
|
374
|
+
f"❌ Command {command} processing failed: {result.get('error')}"
|
|
375
|
+
)
|
|
376
|
+
|
|
334
377
|
except Exception as e:
|
|
335
|
-
self.logger.error(
|
|
336
|
-
|
|
337
|
-
|
|
378
|
+
self.logger.error(
|
|
379
|
+
f"❌ Error handling command {command}: {e}", exc_info=True
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
async def _send_error_response(
|
|
383
|
+
self, webhook: IncomingMessageWebhook, error_details: str
|
|
384
|
+
) -> None:
|
|
338
385
|
"""Send user-friendly error response when processing fails."""
|
|
339
386
|
try:
|
|
340
387
|
user_id = webhook.user.user_id
|
|
341
388
|
message_id = webhook.message.message_id
|
|
342
|
-
|
|
389
|
+
|
|
343
390
|
error_message = (
|
|
344
391
|
"🚨 *Wappa Full Example - Processing Error*\n\n"
|
|
345
392
|
"❌ An error occurred while processing your message.\n"
|
|
@@ -348,26 +395,26 @@ class WappaFullExampleHandler(WappaEventHandler):
|
|
|
348
395
|
"💡 *Tip*: Try using one of our special commands:\n"
|
|
349
396
|
"`/button` • `/list` • `/cta` • `/location`"
|
|
350
397
|
)
|
|
351
|
-
|
|
398
|
+
|
|
352
399
|
result = await self.messenger.send_text(
|
|
353
|
-
recipient=user_id,
|
|
354
|
-
text=error_message,
|
|
355
|
-
reply_to_message_id=message_id
|
|
400
|
+
recipient=user_id, text=error_message, reply_to_message_id=message_id
|
|
356
401
|
)
|
|
357
|
-
|
|
402
|
+
|
|
358
403
|
if result.success:
|
|
359
404
|
self.logger.info(f"🚨 Error response sent to {user_id}")
|
|
360
405
|
else:
|
|
361
406
|
self.logger.error(f"❌ Failed to send error response: {result.error}")
|
|
362
|
-
|
|
407
|
+
|
|
363
408
|
except Exception as e:
|
|
364
409
|
self.logger.error(f"❌ Error sending error response: {e}")
|
|
365
|
-
|
|
410
|
+
|
|
366
411
|
async def _log_processing_stats(self, start_time: float) -> None:
|
|
367
412
|
"""Log processing statistics."""
|
|
368
413
|
processing_time = int((time.time() - start_time) * 1000)
|
|
369
|
-
success_rate = (
|
|
370
|
-
|
|
414
|
+
success_rate = (
|
|
415
|
+
self._successful_processing / max(1, self._total_messages)
|
|
416
|
+
) * 100
|
|
417
|
+
|
|
371
418
|
self.logger.info(
|
|
372
419
|
f"📊 Processing Stats: "
|
|
373
420
|
f"time={processing_time}ms, "
|
|
@@ -379,17 +426,19 @@ class WappaFullExampleHandler(WappaEventHandler):
|
|
|
379
426
|
f"commands={self._commands_processed}, "
|
|
380
427
|
f"interactions={self._interactive_responses}"
|
|
381
428
|
)
|
|
382
|
-
|
|
383
|
-
async def get_handler_statistics(self) ->
|
|
429
|
+
|
|
430
|
+
async def get_handler_statistics(self) -> dict[str, any]:
|
|
384
431
|
"""
|
|
385
432
|
Get comprehensive handler statistics.
|
|
386
|
-
|
|
433
|
+
|
|
387
434
|
Returns:
|
|
388
435
|
Dictionary with processing statistics and handler metrics
|
|
389
436
|
"""
|
|
390
437
|
try:
|
|
391
|
-
success_rate = (
|
|
392
|
-
|
|
438
|
+
success_rate = (
|
|
439
|
+
self._successful_processing / max(1, self._total_messages)
|
|
440
|
+
) * 100
|
|
441
|
+
|
|
393
442
|
stats = {
|
|
394
443
|
"handler_info": {
|
|
395
444
|
"name": "WappaFullExampleHandler",
|
|
@@ -399,41 +448,51 @@ class WappaFullExampleHandler(WappaEventHandler):
|
|
|
399
448
|
"Interactive commands with state management",
|
|
400
449
|
"Media relay functionality",
|
|
401
450
|
"User tracking and analytics",
|
|
402
|
-
"Welcome messages for first-time users"
|
|
403
|
-
]
|
|
451
|
+
"Welcome messages for first-time users",
|
|
452
|
+
],
|
|
404
453
|
},
|
|
405
454
|
"processing_stats": {
|
|
406
455
|
"total_messages": self._total_messages,
|
|
407
456
|
"successful_processing": self._successful_processing,
|
|
408
457
|
"failed_processing": self._failed_processing,
|
|
409
|
-
"success_rate_percent": round(success_rate, 2)
|
|
458
|
+
"success_rate_percent": round(success_rate, 2),
|
|
410
459
|
},
|
|
411
460
|
"feature_usage": {
|
|
412
461
|
"first_time_users": self._first_time_users,
|
|
413
462
|
"commands_processed": self._commands_processed,
|
|
414
|
-
"interactive_responses": self._interactive_responses
|
|
463
|
+
"interactive_responses": self._interactive_responses,
|
|
415
464
|
},
|
|
416
465
|
"supported_commands": ["/button", "/list", "/cta", "/location"],
|
|
417
466
|
"supported_message_types": [
|
|
418
|
-
"text",
|
|
419
|
-
"
|
|
420
|
-
|
|
467
|
+
"text",
|
|
468
|
+
"image",
|
|
469
|
+
"video",
|
|
470
|
+
"audio",
|
|
471
|
+
"voice",
|
|
472
|
+
"document",
|
|
473
|
+
"location",
|
|
474
|
+
"contact",
|
|
475
|
+
"contacts",
|
|
476
|
+
"interactive",
|
|
477
|
+
],
|
|
421
478
|
}
|
|
422
|
-
|
|
479
|
+
|
|
423
480
|
# Add cache statistics if available
|
|
424
481
|
if self.cache_helper:
|
|
425
482
|
cache_stats = await self.cache_helper.get_cache_statistics()
|
|
426
483
|
stats["cache_stats"] = cache_stats
|
|
427
|
-
|
|
484
|
+
|
|
428
485
|
return stats
|
|
429
|
-
|
|
486
|
+
|
|
430
487
|
except Exception as e:
|
|
431
488
|
self.logger.error(f"❌ Error collecting handler statistics: {e}")
|
|
432
489
|
return {"error": f"Statistics collection failed: {str(e)}"}
|
|
433
|
-
|
|
490
|
+
|
|
434
491
|
def __str__(self) -> str:
|
|
435
492
|
"""String representation of the handler."""
|
|
436
|
-
success_rate = (
|
|
493
|
+
success_rate = (
|
|
494
|
+
self._successful_processing / max(1, self._total_messages)
|
|
495
|
+
) * 100
|
|
437
496
|
return (
|
|
438
497
|
f"WappaFullExampleHandler("
|
|
439
498
|
f"messages={self._total_messages}, "
|
|
@@ -442,4 +501,4 @@ class WappaFullExampleHandler(WappaEventHandler):
|
|
|
442
501
|
f"commands={self._commands_processed}, "
|
|
443
502
|
f"interactions={self._interactive_responses}"
|
|
444
503
|
f")"
|
|
445
|
-
)
|
|
504
|
+
)
|