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.

Files changed (126) hide show
  1. wappa/__init__.py +4 -5
  2. wappa/api/controllers/webhook_controller.py +5 -2
  3. wappa/api/dependencies/__init__.py +0 -5
  4. wappa/api/middleware/error_handler.py +4 -4
  5. wappa/api/middleware/owner.py +11 -5
  6. wappa/api/routes/webhooks.py +2 -2
  7. wappa/cli/__init__.py +1 -1
  8. wappa/cli/examples/init/app/main.py +2 -1
  9. wappa/cli/examples/init/app/master_event.py +5 -3
  10. wappa/cli/examples/json_cache_example/app/__init__.py +1 -1
  11. wappa/cli/examples/json_cache_example/app/main.py +56 -44
  12. wappa/cli/examples/json_cache_example/app/master_event.py +181 -145
  13. wappa/cli/examples/json_cache_example/app/models/__init__.py +1 -1
  14. wappa/cli/examples/json_cache_example/app/models/json_demo_models.py +32 -51
  15. wappa/cli/examples/json_cache_example/app/scores/__init__.py +2 -2
  16. wappa/cli/examples/json_cache_example/app/scores/score_base.py +52 -46
  17. wappa/cli/examples/json_cache_example/app/scores/score_cache_statistics.py +70 -62
  18. wappa/cli/examples/json_cache_example/app/scores/score_message_history.py +41 -44
  19. wappa/cli/examples/json_cache_example/app/scores/score_state_commands.py +83 -71
  20. wappa/cli/examples/json_cache_example/app/scores/score_user_management.py +73 -57
  21. wappa/cli/examples/json_cache_example/app/utils/__init__.py +2 -2
  22. wappa/cli/examples/json_cache_example/app/utils/cache_utils.py +54 -56
  23. wappa/cli/examples/json_cache_example/app/utils/message_utils.py +85 -80
  24. wappa/cli/examples/openai_transcript/app/main.py +2 -1
  25. wappa/cli/examples/openai_transcript/app/master_event.py +31 -22
  26. wappa/cli/examples/openai_transcript/app/openai_utils/__init__.py +1 -1
  27. wappa/cli/examples/openai_transcript/app/openai_utils/audio_processing.py +37 -24
  28. wappa/cli/examples/redis_cache_example/app/__init__.py +1 -1
  29. wappa/cli/examples/redis_cache_example/app/main.py +56 -44
  30. wappa/cli/examples/redis_cache_example/app/master_event.py +181 -145
  31. wappa/cli/examples/redis_cache_example/app/models/redis_demo_models.py +31 -50
  32. wappa/cli/examples/redis_cache_example/app/scores/__init__.py +2 -2
  33. wappa/cli/examples/redis_cache_example/app/scores/score_base.py +52 -46
  34. wappa/cli/examples/redis_cache_example/app/scores/score_cache_statistics.py +70 -62
  35. wappa/cli/examples/redis_cache_example/app/scores/score_message_history.py +41 -44
  36. wappa/cli/examples/redis_cache_example/app/scores/score_state_commands.py +83 -71
  37. wappa/cli/examples/redis_cache_example/app/scores/score_user_management.py +73 -57
  38. wappa/cli/examples/redis_cache_example/app/utils/__init__.py +2 -2
  39. wappa/cli/examples/redis_cache_example/app/utils/cache_utils.py +54 -56
  40. wappa/cli/examples/redis_cache_example/app/utils/message_utils.py +85 -80
  41. wappa/cli/examples/simple_echo_example/app/__init__.py +1 -1
  42. wappa/cli/examples/simple_echo_example/app/main.py +41 -33
  43. wappa/cli/examples/simple_echo_example/app/master_event.py +78 -57
  44. wappa/cli/examples/wappa_full_example/app/__init__.py +1 -1
  45. wappa/cli/examples/wappa_full_example/app/handlers/__init__.py +1 -1
  46. wappa/cli/examples/wappa_full_example/app/handlers/command_handlers.py +134 -126
  47. wappa/cli/examples/wappa_full_example/app/handlers/message_handlers.py +237 -229
  48. wappa/cli/examples/wappa_full_example/app/handlers/state_handlers.py +170 -148
  49. wappa/cli/examples/wappa_full_example/app/main.py +51 -39
  50. wappa/cli/examples/wappa_full_example/app/master_event.py +179 -120
  51. wappa/cli/examples/wappa_full_example/app/models/__init__.py +1 -1
  52. wappa/cli/examples/wappa_full_example/app/models/state_models.py +113 -104
  53. wappa/cli/examples/wappa_full_example/app/models/user_models.py +92 -76
  54. wappa/cli/examples/wappa_full_example/app/models/webhook_metadata.py +109 -83
  55. wappa/cli/examples/wappa_full_example/app/utils/__init__.py +1 -1
  56. wappa/cli/examples/wappa_full_example/app/utils/cache_utils.py +132 -113
  57. wappa/cli/examples/wappa_full_example/app/utils/media_handler.py +175 -132
  58. wappa/cli/examples/wappa_full_example/app/utils/metadata_extractor.py +126 -87
  59. wappa/cli/main.py +9 -4
  60. wappa/core/__init__.py +18 -23
  61. wappa/core/config/settings.py +7 -5
  62. wappa/core/events/default_handlers.py +1 -1
  63. wappa/core/factory/wappa_builder.py +38 -25
  64. wappa/core/plugins/redis_plugin.py +1 -3
  65. wappa/core/plugins/wappa_core_plugin.py +7 -6
  66. wappa/core/types.py +12 -12
  67. wappa/core/wappa_app.py +10 -8
  68. wappa/database/__init__.py +3 -4
  69. wappa/domain/enums/messenger_platform.py +1 -2
  70. wappa/domain/factories/media_factory.py +5 -20
  71. wappa/domain/factories/message_factory.py +5 -20
  72. wappa/domain/factories/messenger_factory.py +2 -4
  73. wappa/domain/interfaces/cache_interface.py +7 -7
  74. wappa/domain/interfaces/media_interface.py +2 -5
  75. wappa/domain/models/media_result.py +1 -3
  76. wappa/domain/models/platforms/platform_config.py +1 -3
  77. wappa/messaging/__init__.py +9 -12
  78. wappa/messaging/whatsapp/handlers/whatsapp_media_handler.py +20 -22
  79. wappa/models/__init__.py +27 -35
  80. wappa/persistence/__init__.py +12 -15
  81. wappa/persistence/cache_factory.py +0 -1
  82. wappa/persistence/json/__init__.py +1 -1
  83. wappa/persistence/json/cache_adapters.py +37 -25
  84. wappa/persistence/json/handlers/state_handler.py +60 -52
  85. wappa/persistence/json/handlers/table_handler.py +51 -49
  86. wappa/persistence/json/handlers/user_handler.py +71 -55
  87. wappa/persistence/json/handlers/utils/file_manager.py +42 -39
  88. wappa/persistence/json/handlers/utils/key_factory.py +1 -1
  89. wappa/persistence/json/handlers/utils/serialization.py +13 -11
  90. wappa/persistence/json/json_cache_factory.py +4 -8
  91. wappa/persistence/json/storage_manager.py +66 -79
  92. wappa/persistence/memory/__init__.py +1 -1
  93. wappa/persistence/memory/cache_adapters.py +37 -25
  94. wappa/persistence/memory/handlers/state_handler.py +62 -52
  95. wappa/persistence/memory/handlers/table_handler.py +59 -53
  96. wappa/persistence/memory/handlers/user_handler.py +75 -55
  97. wappa/persistence/memory/handlers/utils/key_factory.py +1 -1
  98. wappa/persistence/memory/handlers/utils/memory_store.py +75 -71
  99. wappa/persistence/memory/handlers/utils/ttl_manager.py +59 -67
  100. wappa/persistence/memory/memory_cache_factory.py +3 -7
  101. wappa/persistence/memory/storage_manager.py +52 -62
  102. wappa/persistence/redis/cache_adapters.py +27 -21
  103. wappa/persistence/redis/ops.py +11 -11
  104. wappa/persistence/redis/redis_client.py +4 -6
  105. wappa/persistence/redis/redis_manager.py +12 -4
  106. wappa/processors/factory.py +5 -5
  107. wappa/schemas/factory.py +2 -5
  108. wappa/schemas/whatsapp/message_types/errors.py +3 -12
  109. wappa/schemas/whatsapp/validators.py +3 -3
  110. wappa/webhooks/__init__.py +17 -18
  111. wappa/webhooks/factory.py +3 -5
  112. wappa/webhooks/whatsapp/__init__.py +10 -13
  113. wappa/webhooks/whatsapp/message_types/audio.py +0 -4
  114. wappa/webhooks/whatsapp/message_types/document.py +1 -9
  115. wappa/webhooks/whatsapp/message_types/errors.py +3 -12
  116. wappa/webhooks/whatsapp/message_types/location.py +1 -21
  117. wappa/webhooks/whatsapp/message_types/sticker.py +1 -5
  118. wappa/webhooks/whatsapp/message_types/text.py +0 -6
  119. wappa/webhooks/whatsapp/message_types/video.py +1 -20
  120. wappa/webhooks/whatsapp/status_models.py +2 -2
  121. wappa/webhooks/whatsapp/validators.py +3 -3
  122. {wappa-0.1.9.dist-info → wappa-0.1.10.dist-info}/METADATA +362 -8
  123. {wappa-0.1.9.dist-info → wappa-0.1.10.dist-info}/RECORD +126 -126
  124. {wappa-0.1.9.dist-info → wappa-0.1.10.dist-info}/WHEEL +0 -0
  125. {wappa-0.1.9.dist-info → wappa-0.1.10.dist-info}/entry_points.txt +0 -0
  126. {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: Optional[CacheHelper] = None
47
- self.message_handlers: Optional[MessageHandlers] = None
48
- self.command_handlers: Optional[CommandHandlers] = None
49
- self.state_handlers: Optional[StateHandlers] = None
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("🚀 WappaFullExampleHandler initialized - comprehensive demo ready")
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("Failed to handle user profile, continuing without caching")
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(f"✅ Interactive state handled: {state_result.get('selection_type', 'unknown')}")
117
+ self.logger.info(
118
+ f"✅ Interactive state handled: {state_result.get('selection_type', 'unknown')}"
119
+ )
116
120
  else:
117
- self.logger.info(f"🔄 Interactive state reminder sent: {state_result.get('error', 'unknown')}")
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(f"❌ {message_type} message processing failed: {result.get('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(webhook, result.get('error', 'Processing failed'))
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(f"❌ Critical error in message processing: {e}", exc_info=True)
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(f"📊 Message status: {status_value.upper()} for {recipient} (msg: {message_id[:20]}...)")
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("❌ Cache factory not available - Redis caching unavailable")
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(self.messenger, self.cache_factory, self.logger)
221
- self.command_handlers = CommandHandlers(self.messenger, self.cache_factory, self.logger)
222
- self.state_handlers = StateHandlers(self.messenger, self.cache_factory, self.logger)
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(self, webhook: IncomingMessageWebhook) -> Optional[UserProfile]:
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 user_profile.is_first_time_user and not user_profile.has_received_welcome:
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(self, webhook: IncomingMessageWebhook, user_profile: UserProfile) -> None:
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(f"👋 Welcome message sent to {display_name} ({user_id})")
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(f"✅ Message marked as read: {webhook.message.message_id[:20]}...")
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(self, webhook: IncomingMessageWebhook,
314
- user_profile: UserProfile, command: str) -> None:
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(webhook, user_profile)
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(webhook, user_profile)
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(webhook, user_profile)
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(webhook, user_profile)
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(f"❌ Command {command} processing failed: {result.get('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(f"❌ Error handling command {command}: {e}", exc_info=True)
336
-
337
- async def _send_error_response(self, webhook: IncomingMessageWebhook, error_details: str) -> None:
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 = (self._successful_processing / max(1, self._total_messages)) * 100
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) -> Dict[str, any]:
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 = (self._successful_processing / max(1, self._total_messages)) * 100
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", "image", "video", "audio", "voice", "document",
419
- "location", "contact", "contacts", "interactive"
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 = (self._successful_processing / max(1, self._total_messages)) * 100
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
+ )
@@ -2,4 +2,4 @@
2
2
  Pydantic models for the Wappa Full Example application.
3
3
 
4
4
  Contains models for webhook metadata, user tracking, and interactive states.
5
- """
5
+ """