wappa 0.1.8__py3-none-any.whl → 0.1.9__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 (78) hide show
  1. wappa/cli/examples/init/.env.example +33 -0
  2. wappa/cli/examples/init/app/__init__.py +0 -0
  3. wappa/cli/examples/init/app/main.py +8 -0
  4. wappa/cli/examples/init/app/master_event.py +8 -0
  5. wappa/cli/examples/json_cache_example/.env.example +33 -0
  6. wappa/cli/examples/json_cache_example/app/__init__.py +1 -0
  7. wappa/cli/examples/json_cache_example/app/main.py +235 -0
  8. wappa/cli/examples/json_cache_example/app/master_event.py +419 -0
  9. wappa/cli/examples/json_cache_example/app/models/__init__.py +1 -0
  10. wappa/cli/examples/json_cache_example/app/models/json_demo_models.py +275 -0
  11. wappa/cli/examples/json_cache_example/app/scores/__init__.py +35 -0
  12. wappa/cli/examples/json_cache_example/app/scores/score_base.py +186 -0
  13. wappa/cli/examples/json_cache_example/app/scores/score_cache_statistics.py +248 -0
  14. wappa/cli/examples/json_cache_example/app/scores/score_message_history.py +190 -0
  15. wappa/cli/examples/json_cache_example/app/scores/score_state_commands.py +260 -0
  16. wappa/cli/examples/json_cache_example/app/scores/score_user_management.py +223 -0
  17. wappa/cli/examples/json_cache_example/app/utils/__init__.py +26 -0
  18. wappa/cli/examples/json_cache_example/app/utils/cache_utils.py +176 -0
  19. wappa/cli/examples/json_cache_example/app/utils/message_utils.py +246 -0
  20. wappa/cli/examples/openai_transcript/.gitignore +63 -4
  21. wappa/cli/examples/openai_transcript/app/__init__.py +0 -0
  22. wappa/cli/examples/openai_transcript/app/main.py +8 -0
  23. wappa/cli/examples/openai_transcript/app/master_event.py +53 -0
  24. wappa/cli/examples/openai_transcript/app/openai_utils/__init__.py +3 -0
  25. wappa/cli/examples/openai_transcript/app/openai_utils/audio_processing.py +76 -0
  26. wappa/cli/examples/redis_cache_example/.env.example +33 -0
  27. wappa/cli/examples/redis_cache_example/app/__init__.py +6 -0
  28. wappa/cli/examples/redis_cache_example/app/main.py +234 -0
  29. wappa/cli/examples/redis_cache_example/app/master_event.py +419 -0
  30. wappa/cli/examples/redis_cache_example/app/models/redis_demo_models.py +275 -0
  31. wappa/cli/examples/redis_cache_example/app/scores/__init__.py +35 -0
  32. wappa/cli/examples/redis_cache_example/app/scores/score_base.py +186 -0
  33. wappa/cli/examples/redis_cache_example/app/scores/score_cache_statistics.py +248 -0
  34. wappa/cli/examples/redis_cache_example/app/scores/score_message_history.py +190 -0
  35. wappa/cli/examples/redis_cache_example/app/scores/score_state_commands.py +260 -0
  36. wappa/cli/examples/redis_cache_example/app/scores/score_user_management.py +223 -0
  37. wappa/cli/examples/redis_cache_example/app/utils/__init__.py +26 -0
  38. wappa/cli/examples/redis_cache_example/app/utils/cache_utils.py +176 -0
  39. wappa/cli/examples/redis_cache_example/app/utils/message_utils.py +246 -0
  40. wappa/cli/examples/simple_echo_example/.env.example +33 -0
  41. wappa/cli/examples/simple_echo_example/app/__init__.py +7 -0
  42. wappa/cli/examples/simple_echo_example/app/main.py +183 -0
  43. wappa/cli/examples/simple_echo_example/app/master_event.py +209 -0
  44. wappa/cli/examples/wappa_full_example/.env.example +33 -0
  45. wappa/cli/examples/wappa_full_example/.gitignore +63 -4
  46. wappa/cli/examples/wappa_full_example/app/__init__.py +6 -0
  47. wappa/cli/examples/wappa_full_example/app/handlers/__init__.py +5 -0
  48. wappa/cli/examples/wappa_full_example/app/handlers/command_handlers.py +484 -0
  49. wappa/cli/examples/wappa_full_example/app/handlers/message_handlers.py +551 -0
  50. wappa/cli/examples/wappa_full_example/app/handlers/state_handlers.py +492 -0
  51. wappa/cli/examples/wappa_full_example/app/main.py +257 -0
  52. wappa/cli/examples/wappa_full_example/app/master_event.py +445 -0
  53. wappa/cli/examples/wappa_full_example/app/media/README.md +54 -0
  54. wappa/cli/examples/wappa_full_example/app/media/buttons/README.md +62 -0
  55. wappa/cli/examples/wappa_full_example/app/media/buttons/kitty.png +0 -0
  56. wappa/cli/examples/wappa_full_example/app/media/buttons/puppy.png +0 -0
  57. wappa/cli/examples/wappa_full_example/app/media/list/README.md +110 -0
  58. wappa/cli/examples/wappa_full_example/app/media/list/audio.mp3 +0 -0
  59. wappa/cli/examples/wappa_full_example/app/media/list/document.pdf +0 -0
  60. wappa/cli/examples/wappa_full_example/app/media/list/image.png +0 -0
  61. wappa/cli/examples/wappa_full_example/app/media/list/video.mp4 +0 -0
  62. wappa/cli/examples/wappa_full_example/app/models/__init__.py +5 -0
  63. wappa/cli/examples/wappa_full_example/app/models/state_models.py +425 -0
  64. wappa/cli/examples/wappa_full_example/app/models/user_models.py +287 -0
  65. wappa/cli/examples/wappa_full_example/app/models/webhook_metadata.py +301 -0
  66. wappa/cli/examples/wappa_full_example/app/utils/__init__.py +5 -0
  67. wappa/cli/examples/wappa_full_example/app/utils/cache_utils.py +483 -0
  68. wappa/cli/examples/wappa_full_example/app/utils/media_handler.py +473 -0
  69. wappa/cli/examples/wappa_full_example/app/utils/metadata_extractor.py +298 -0
  70. wappa/cli/main.py +8 -4
  71. {wappa-0.1.8.dist-info → wappa-0.1.9.dist-info}/METADATA +1 -1
  72. {wappa-0.1.8.dist-info → wappa-0.1.9.dist-info}/RECORD +75 -11
  73. wappa/cli/examples/init/pyproject.toml +0 -7
  74. wappa/cli/examples/simple_echo_example/.python-version +0 -1
  75. wappa/cli/examples/simple_echo_example/pyproject.toml +0 -9
  76. {wappa-0.1.8.dist-info → wappa-0.1.9.dist-info}/WHEEL +0 -0
  77. {wappa-0.1.8.dist-info → wappa-0.1.9.dist-info}/entry_points.txt +0 -0
  78. {wappa-0.1.8.dist-info → wappa-0.1.9.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,445 @@
1
+ """
2
+ Master Event Handler for Wappa Full Example Application
3
+
4
+ This is the main WappaEventHandler implementation that demonstrates comprehensive
5
+ WhatsApp Business API functionality including metadata extraction, interactive
6
+ commands, state management, and all message type handling.
7
+ """
8
+
9
+ import time
10
+ from typing import Dict, Optional
11
+
12
+ from wappa import WappaEventHandler
13
+ from wappa.webhooks import ErrorWebhook, IncomingMessageWebhook, StatusWebhook
14
+
15
+ from .handlers.command_handlers import (
16
+ CommandHandlers,
17
+ get_command_from_text,
18
+ is_special_command,
19
+ )
20
+ from .handlers.message_handlers import MessageHandlers, handle_message_by_type
21
+ from .handlers.state_handlers import StateHandlers, handle_user_in_state
22
+ from .models.state_models import StateType
23
+ from .models.user_models import UserProfile
24
+ from .utils.cache_utils import CacheHelper
25
+
26
+
27
+ class WappaFullExampleHandler(WappaEventHandler):
28
+ """
29
+ Comprehensive WappaEventHandler implementation demonstrating all framework features.
30
+
31
+ Features:
32
+ - Complete message type handling with metadata extraction
33
+ - Interactive commands (/button, /list, /cta, /location) with state management
34
+ - Media relay functionality using media_id
35
+ - User tracking and analytics with Redis cache
36
+ - Professional error handling and logging
37
+ - Welcome messages for first-time users
38
+ - State-based interactive workflows with TTL
39
+ """
40
+
41
+ def __init__(self):
42
+ """Initialize the comprehensive Wappa example handler."""
43
+ super().__init__()
44
+
45
+ # 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
+
51
+ # Statistics
52
+ self._total_messages = 0
53
+ self._successful_processing = 0
54
+ self._failed_processing = 0
55
+ self._first_time_users = 0
56
+ self._commands_processed = 0
57
+ self._interactive_responses = 0
58
+
59
+ self.logger.info("🚀 WappaFullExampleHandler initialized - comprehensive demo ready")
60
+
61
+ async def process_message(self, webhook: IncomingMessageWebhook) -> None:
62
+ """
63
+ Main message processing method with comprehensive functionality.
64
+
65
+ This method orchestrates:
66
+ 1. Dependency validation and setup
67
+ 2. User profile management and welcome messages
68
+ 3. Message reading acknowledgment
69
+ 4. Interactive state checking and processing
70
+ 5. Special command handling
71
+ 6. Regular message processing with metadata extraction
72
+ 7. Statistics tracking and logging
73
+
74
+ Args:
75
+ webhook: IncomingMessageWebhook containing message data
76
+ """
77
+ start_time = time.time()
78
+ self._total_messages += 1
79
+
80
+ try:
81
+ # 1. Setup dependencies and validate
82
+ if not await self._setup_dependencies():
83
+ self._failed_processing += 1
84
+ return
85
+
86
+ # 2. Handle user profile and first-time user welcome
87
+ user_profile = await self._handle_user_profile_and_welcome(webhook)
88
+ if not user_profile:
89
+ self.logger.warning("Failed to handle user profile, continuing without caching")
90
+ user_profile = UserProfile(phone_number=webhook.user.user_id)
91
+
92
+ # 3. Mark message as read (as specified in requirements)
93
+ await self._mark_message_as_read(webhook)
94
+
95
+ # Extract basic message info for routing
96
+ user_id = webhook.user.user_id
97
+ message_text = webhook.get_message_text().strip()
98
+ message_type = webhook.get_message_type_name()
99
+
100
+ self.logger.info(
101
+ f"📨 Processing {message_type} from {user_id}: "
102
+ f"'{message_text[:50]}{'...' if len(message_text) > 50 else ''}'"
103
+ )
104
+
105
+ # 4. Check for active interactive states first
106
+ state_result = await handle_user_in_state(
107
+ webhook, user_profile, self.messenger, self.cache_factory, self.logger
108
+ )
109
+
110
+ if state_result is not None:
111
+ # User was in an interactive state - handle accordingly
112
+ if state_result["success"]:
113
+ self._interactive_responses += 1
114
+ self._successful_processing += 1
115
+ self.logger.info(f"✅ Interactive state handled: {state_result.get('selection_type', 'unknown')}")
116
+ else:
117
+ self.logger.info(f"🔄 Interactive state reminder sent: {state_result.get('error', 'unknown')}")
118
+
119
+ await self._log_processing_stats(start_time)
120
+ return
121
+
122
+ # 5. Check for special commands (only for text messages)
123
+ if message_type == "text" and is_special_command(message_text):
124
+ command = get_command_from_text(message_text)
125
+ await self._handle_special_command(webhook, user_profile, command)
126
+ self._commands_processed += 1
127
+ self._successful_processing += 1
128
+ await self._log_processing_stats(start_time)
129
+ return
130
+
131
+ # 6. Handle regular message processing with metadata
132
+ result = await handle_message_by_type(
133
+ webhook, user_profile, self.messenger, self.cache_factory, self.logger
134
+ )
135
+
136
+ if result["success"]:
137
+ self._successful_processing += 1
138
+ self.logger.info(f"✅ {message_type} message processed successfully")
139
+ else:
140
+ self._failed_processing += 1
141
+ self.logger.error(f"❌ {message_type} message processing failed: {result.get('error')}")
142
+
143
+ # Send error response to user
144
+ await self._send_error_response(webhook, result.get('error', 'Processing failed'))
145
+
146
+ await self._log_processing_stats(start_time)
147
+
148
+ except Exception as e:
149
+ self._failed_processing += 1
150
+ self.logger.error(f"❌ Critical error in message processing: {e}", exc_info=True)
151
+
152
+ # Send generic error response
153
+ try:
154
+ await self.messenger.send_text(
155
+ recipient=webhook.user.user_id,
156
+ text="❌ Sorry, something went wrong processing your message. Please try again.",
157
+ reply_to_message_id=webhook.message.message_id
158
+ )
159
+ except Exception as send_error:
160
+ self.logger.error(f"❌ Failed to send error response: {send_error}")
161
+
162
+ async def process_status(self, webhook: StatusWebhook) -> None:
163
+ """
164
+ Process status webhooks from WhatsApp Business API.
165
+
166
+ Args:
167
+ webhook: StatusWebhook containing delivery status information
168
+ """
169
+ try:
170
+ status_value = webhook.status.value
171
+ recipient = webhook.recipient_id
172
+ message_id = webhook.message_id
173
+
174
+ self.logger.info(f"📊 Message status: {status_value.upper()} for {recipient} (msg: {message_id[:20]}...)")
175
+
176
+ # You can add custom status processing here
177
+ # For example: update delivery statistics, handle failed deliveries, etc.
178
+
179
+ except Exception as e:
180
+ self.logger.error(f"❌ Error processing status webhook: {e}", exc_info=True)
181
+
182
+ async def process_error(self, webhook: ErrorWebhook) -> None:
183
+ """
184
+ Process error webhooks from WhatsApp Business API.
185
+
186
+ Args:
187
+ webhook: ErrorWebhook containing error information
188
+ """
189
+ try:
190
+ error_count = webhook.get_error_count()
191
+ primary_error = webhook.get_primary_error()
192
+
193
+ self.logger.error(
194
+ f"🚨 WhatsApp API error: {error_count} errors, "
195
+ f"primary: {primary_error.error_code} - {primary_error.error_title}"
196
+ )
197
+
198
+ # Record error in statistics
199
+ self._failed_processing += 1
200
+
201
+ # You can add custom error handling logic here
202
+ # For example: alerting systems, retry mechanisms, etc.
203
+
204
+ except Exception as e:
205
+ self.logger.error(f"❌ Error processing error webhook: {e}", exc_info=True)
206
+
207
+ async def _setup_dependencies(self) -> bool:
208
+ """Setup handler dependencies and validate state."""
209
+ if not self.validate_dependencies():
210
+ self.logger.error("❌ Dependencies not properly injected")
211
+ return False
212
+
213
+ if not self.cache_factory:
214
+ self.logger.error("❌ Cache factory not available - Redis caching unavailable")
215
+ return False
216
+
217
+ try:
218
+ # Initialize helper instances
219
+ 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
+
224
+ self.logger.debug("✅ Handler dependencies initialized successfully")
225
+ return True
226
+
227
+ except Exception as e:
228
+ self.logger.error(f"❌ Failed to setup dependencies: {e}", exc_info=True)
229
+ return False
230
+
231
+ async def _handle_user_profile_and_welcome(self, webhook: IncomingMessageWebhook) -> Optional[UserProfile]:
232
+ """Handle user profile caching and send welcome message to first-time users."""
233
+ try:
234
+ user_id = webhook.user.user_id
235
+ user_name = webhook.user.profile_name
236
+
237
+ # Get or create user profile
238
+ user_profile = await self.cache_helper.get_or_create_user_profile(
239
+ user_id, user_name, user_name
240
+ )
241
+
242
+ # Send welcome message to first-time users
243
+ if user_profile.is_first_time_user and not user_profile.has_received_welcome:
244
+ await self._send_welcome_message(webhook, user_profile)
245
+ user_profile.mark_welcome_sent()
246
+ await self.cache_helper.save_user_profile(user_profile)
247
+ self._first_time_users += 1
248
+
249
+ return user_profile
250
+
251
+ except Exception as e:
252
+ self.logger.error(f"❌ Error handling user profile: {e}", exc_info=True)
253
+ return None
254
+
255
+ async def _send_welcome_message(self, webhook: IncomingMessageWebhook, user_profile: UserProfile) -> None:
256
+ """Send welcome message with instructions to first-time users."""
257
+ user_id = webhook.user.user_id
258
+ display_name = user_profile.get_display_name()
259
+
260
+ welcome_text = (
261
+ f"🎉 *Welcome to Wappa Full Example, {display_name}!*\n\n"
262
+ f"This is a comprehensive demonstration of the Wappa framework capabilities.\n\n"
263
+ f"🚀 *What this example does:*\n"
264
+ f"• Echoes all message types with detailed metadata\n"
265
+ f"• Demonstrates interactive features (buttons, lists, CTA, locations)\n"
266
+ f"• Shows state management with TTL\n"
267
+ f"• Tracks user activity with Redis cache\n"
268
+ f"• Handles media relay using media_id\n\n"
269
+ f"🎯 *Try these special commands:*\n"
270
+ f"• `/button` - Interactive button demo with animal selection\n"
271
+ f"• `/list` - Interactive list demo with media files\n"
272
+ f"• `/cta` - Call-to-action button with external link\n"
273
+ f"• `/location` - Location sharing demonstration\n\n"
274
+ f"📨 *Send any message type to see it echoed with metadata:*\n"
275
+ f"• Text messages → Echo with metadata\n"
276
+ f"• Images/Videos/Audio/Documents → Relayed using media_id\n"
277
+ f"• Locations → Same location echoed back\n"
278
+ f"• Contacts → Contact information echoed back\n\n"
279
+ f"💡 *Pro tip*: This demo showcases production-ready patterns for building WhatsApp Business applications!"
280
+ )
281
+
282
+ try:
283
+ result = await self.messenger.send_text(
284
+ recipient=user_id,
285
+ text=welcome_text,
286
+ reply_to_message_id=webhook.message.message_id
287
+ )
288
+
289
+ if result.success:
290
+ self.logger.info(f"👋 Welcome message sent to {display_name} ({user_id})")
291
+ else:
292
+ self.logger.error(f"❌ Failed to send welcome message: {result.error}")
293
+
294
+ except Exception as e:
295
+ self.logger.error(f"❌ Error sending welcome message: {e}")
296
+
297
+ async def _mark_message_as_read(self, webhook: IncomingMessageWebhook) -> None:
298
+ """Mark incoming message as read (as specified in requirements)."""
299
+ try:
300
+ result = await self.messenger.mark_as_read(
301
+ message_id=webhook.message.message_id,
302
+ typing=False
303
+ )
304
+
305
+ if result.success:
306
+ self.logger.debug(f"✅ Message marked as read: {webhook.message.message_id[:20]}...")
307
+ else:
308
+ self.logger.warning(f"⚠️ Failed to mark message as read: {result.error}")
309
+
310
+ except Exception as e:
311
+ 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:
315
+ """Handle special commands using command handlers."""
316
+ try:
317
+ if command == "/button":
318
+ result = await self.command_handlers.handle_button_command(webhook, user_profile)
319
+ elif command == "/list":
320
+ result = await self.command_handlers.handle_list_command(webhook, user_profile)
321
+ elif command == "/cta":
322
+ result = await self.command_handlers.handle_cta_command(webhook, user_profile)
323
+ elif command == "/location":
324
+ result = await self.command_handlers.handle_location_command(webhook, user_profile)
325
+ else:
326
+ self.logger.warning(f"Unsupported command: {command}")
327
+ return
328
+
329
+ if result["success"]:
330
+ self.logger.info(f"✅ Command {command} processed successfully")
331
+ else:
332
+ self.logger.error(f"❌ Command {command} processing failed: {result.get('error')}")
333
+
334
+ 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:
338
+ """Send user-friendly error response when processing fails."""
339
+ try:
340
+ user_id = webhook.user.user_id
341
+ message_id = webhook.message.message_id
342
+
343
+ error_message = (
344
+ "🚨 *Wappa Full Example - Processing Error*\n\n"
345
+ "❌ An error occurred while processing your message.\n"
346
+ "Our comprehensive demo system encountered an issue.\n\n"
347
+ "🔄 *Please try again* or contact support if the problem persists.\n\n"
348
+ "💡 *Tip*: Try using one of our special commands:\n"
349
+ "`/button` • `/list` • `/cta` • `/location`"
350
+ )
351
+
352
+ result = await self.messenger.send_text(
353
+ recipient=user_id,
354
+ text=error_message,
355
+ reply_to_message_id=message_id
356
+ )
357
+
358
+ if result.success:
359
+ self.logger.info(f"🚨 Error response sent to {user_id}")
360
+ else:
361
+ self.logger.error(f"❌ Failed to send error response: {result.error}")
362
+
363
+ except Exception as e:
364
+ self.logger.error(f"❌ Error sending error response: {e}")
365
+
366
+ async def _log_processing_stats(self, start_time: float) -> None:
367
+ """Log processing statistics."""
368
+ processing_time = int((time.time() - start_time) * 1000)
369
+ success_rate = (self._successful_processing / max(1, self._total_messages)) * 100
370
+
371
+ self.logger.info(
372
+ f"📊 Processing Stats: "
373
+ f"time={processing_time}ms, "
374
+ f"total={self._total_messages}, "
375
+ f"success={self._successful_processing}, "
376
+ f"failed={self._failed_processing}, "
377
+ f"rate={success_rate:.1f}%, "
378
+ f"new_users={self._first_time_users}, "
379
+ f"commands={self._commands_processed}, "
380
+ f"interactions={self._interactive_responses}"
381
+ )
382
+
383
+ async def get_handler_statistics(self) -> Dict[str, any]:
384
+ """
385
+ Get comprehensive handler statistics.
386
+
387
+ Returns:
388
+ Dictionary with processing statistics and handler metrics
389
+ """
390
+ try:
391
+ success_rate = (self._successful_processing / max(1, self._total_messages)) * 100
392
+
393
+ stats = {
394
+ "handler_info": {
395
+ "name": "WappaFullExampleHandler",
396
+ "description": "Comprehensive WhatsApp Business API demo",
397
+ "features": [
398
+ "Complete message type handling",
399
+ "Interactive commands with state management",
400
+ "Media relay functionality",
401
+ "User tracking and analytics",
402
+ "Welcome messages for first-time users"
403
+ ]
404
+ },
405
+ "processing_stats": {
406
+ "total_messages": self._total_messages,
407
+ "successful_processing": self._successful_processing,
408
+ "failed_processing": self._failed_processing,
409
+ "success_rate_percent": round(success_rate, 2)
410
+ },
411
+ "feature_usage": {
412
+ "first_time_users": self._first_time_users,
413
+ "commands_processed": self._commands_processed,
414
+ "interactive_responses": self._interactive_responses
415
+ },
416
+ "supported_commands": ["/button", "/list", "/cta", "/location"],
417
+ "supported_message_types": [
418
+ "text", "image", "video", "audio", "voice", "document",
419
+ "location", "contact", "contacts", "interactive"
420
+ ]
421
+ }
422
+
423
+ # Add cache statistics if available
424
+ if self.cache_helper:
425
+ cache_stats = await self.cache_helper.get_cache_statistics()
426
+ stats["cache_stats"] = cache_stats
427
+
428
+ return stats
429
+
430
+ except Exception as e:
431
+ self.logger.error(f"❌ Error collecting handler statistics: {e}")
432
+ return {"error": f"Statistics collection failed: {str(e)}"}
433
+
434
+ def __str__(self) -> str:
435
+ """String representation of the handler."""
436
+ success_rate = (self._successful_processing / max(1, self._total_messages)) * 100
437
+ return (
438
+ f"WappaFullExampleHandler("
439
+ f"messages={self._total_messages}, "
440
+ f"success_rate={success_rate:.1f}%, "
441
+ f"new_users={self._first_time_users}, "
442
+ f"commands={self._commands_processed}, "
443
+ f"interactions={self._interactive_responses}"
444
+ f")"
445
+ )
@@ -0,0 +1,54 @@
1
+ # Media Files Directory
2
+
3
+ This directory contains media files used by the Wappa Full Example application for interactive demonstrations.
4
+
5
+ ## Directory Structure
6
+
7
+ ```
8
+ media/
9
+ ├── buttons/ # Media files for button command responses
10
+ │ ├── kitty.png # Image sent when user selects "Kitty" button
11
+ │ └── puppy.png # Image sent when user selects "Puppy" button
12
+ └── list/ # Media files for list command responses
13
+ ├── image.png # Sample image file for list selection
14
+ ├── video.mp4 # Sample video file for list selection
15
+ ├── audio.mp3 # Sample audio file for list selection
16
+ └── document.pdf # Sample document file for list selection
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ The application automatically serves these files when users interact with:
22
+
23
+ 1. **Button Command** (`/button`):
24
+ - User selects "🐱 Kitty" → sends `buttons/kitty.png`
25
+ - User selects "🐶 Puppy" → sends `buttons/puppy.png`
26
+
27
+ 2. **List Command** (`/list`):
28
+ - User selects "🖼️ Image" → sends `list/image.png`
29
+ - User selects "🎬 Video" → sends `list/video.mp4`
30
+ - User selects "🎵 Audio" → sends `list/audio.mp3`
31
+ - User selects "📄 Document" → sends `list/document.pdf`
32
+
33
+ ## File Requirements
34
+
35
+ - **Images**: PNG, JPG formats (max 5MB)
36
+ - **Videos**: MP4 format (max 16MB)
37
+ - **Audio**: MP3, OGG formats (max 16MB)
38
+ - **Documents**: PDF format (max 100MB)
39
+
40
+ ## Adding Your Own Files
41
+
42
+ Replace the placeholder files with your own media:
43
+
44
+ 1. Add your files to the appropriate subdirectories
45
+ 2. Use the exact filenames as listed above
46
+ 3. Ensure files meet WhatsApp Business API size limits
47
+ 4. Test with the interactive commands to verify functionality
48
+
49
+ ## Notes
50
+
51
+ - Files are loaded from the local filesystem
52
+ - The media handler automatically detects file types
53
+ - If files are missing, fallback text messages are sent instead
54
+ - This is a demonstration setup - in production, you might use cloud storage or CDN
@@ -0,0 +1,62 @@
1
+ # Button Command Media Files
2
+
3
+ This directory contains media files sent in response to button selections in the `/button` command demo.
4
+
5
+ ## Required Files
6
+
7
+ Place these files in this directory:
8
+
9
+ ### `kitty.png`
10
+ - **Purpose**: Sent when user clicks the "🐱 Kitty" button
11
+ - **Format**: PNG or JPG image
12
+ - **Size limit**: 5MB maximum
13
+ - **Dimensions**: Recommended 500x500px or similar
14
+ - **Content**: Image of a cute kitten
15
+
16
+ ### `puppy.png`
17
+ - **Purpose**: Sent when user clicks the "🐶 Puppy" button
18
+ - **Format**: PNG or JPG image
19
+ - **Size limit**: 5MB maximum
20
+ - **Dimensions**: Recommended 500x500px or similar
21
+ - **Content**: Image of a cute puppy
22
+
23
+ ## How It Works
24
+
25
+ 1. User sends `/button` command
26
+ 2. App creates interactive button message with "Kitty" and "Puppy" options
27
+ 3. User clicks one of the buttons
28
+ 4. App sends the corresponding image file from this directory
29
+ 5. User receives the image with a caption
30
+
31
+ ## File Sources
32
+
33
+ You can add your own images:
34
+ - Download free images from Unsplash, Pixabay, or similar
35
+ - Use your own photos
36
+ - Ensure you have rights to use the images
37
+ - Keep file sizes reasonable for WhatsApp
38
+
39
+ ## Fallback Behavior
40
+
41
+ If files are missing:
42
+ - App will send a text message instead
43
+ - Message will indicate the file is missing
44
+ - Button functionality will still work, but without media
45
+
46
+ ## Example Implementation
47
+
48
+ ```python
49
+ # In state_handlers.py
50
+ if selection_id == "kitty":
51
+ media_file = "kitty.png"
52
+ elif selection_id == "puppy":
53
+ media_file = "puppy.png"
54
+
55
+ media_result = await send_local_media_file(
56
+ messenger=self.messenger,
57
+ recipient=user_id,
58
+ filename=media_file,
59
+ media_subdir="buttons",
60
+ caption=f"Here's your {selection_id}! 🎉"
61
+ )
62
+ ```
@@ -0,0 +1,110 @@
1
+ # List Command Media Files
2
+
3
+ This directory contains media files sent in response to list selections in the `/list` command demo.
4
+
5
+ ## Required Files
6
+
7
+ Place these files in this directory:
8
+
9
+ ### `image.png`
10
+ - **Purpose**: Sent when user selects "🖼️ Image" from the list
11
+ - **Format**: PNG or JPG image
12
+ - **Size limit**: 5MB maximum
13
+ - **Content**: Sample image to demonstrate image sending
14
+ - **Suggested**: Colorful demo image, infographic, or screenshot
15
+
16
+ ### `video.mp4`
17
+ - **Purpose**: Sent when user selects "🎬 Video" from the list
18
+ - **Format**: MP4 video file
19
+ - **Size limit**: 16MB maximum
20
+ - **Duration**: Keep under 60 seconds for demo purposes
21
+ - **Content**: Sample video demonstrating video messaging
22
+ - **Suggested**: Short demo video, animation, or screen recording
23
+
24
+ ### `audio.mp3`
25
+ - **Purpose**: Sent when user selects "🎵 Audio" from the list
26
+ - **Format**: MP3, OGG, or AAC audio file
27
+ - **Size limit**: 16MB maximum
28
+ - **Duration**: Keep under 2 minutes for demo purposes
29
+ - **Content**: Sample audio demonstrating audio messaging
30
+ - **Suggested**: Music clip, voice recording, or sound effect
31
+
32
+ ### `document.pdf`
33
+ - **Purpose**: Sent when user selects "📄 Document" from the list
34
+ - **Format**: PDF document
35
+ - **Size limit**: 100MB maximum
36
+ - **Content**: Sample document demonstrating document sharing
37
+ - **Suggested**: User guide, specification sheet, or informational PDF
38
+
39
+ ## How It Works
40
+
41
+ 1. User sends `/list` command
42
+ 2. App creates interactive list message with 4 media type options
43
+ 3. User selects one option from the list
44
+ 4. App sends the corresponding media file from this directory
45
+ 5. User receives the media file with a caption
46
+
47
+ ## Interactive List Structure
48
+
49
+ ```json
50
+ {
51
+ "title": "📁 Media Files",
52
+ "rows": [
53
+ {"id": "image_file", "title": "🖼️ Image", "description": "Get a sample image file"},
54
+ {"id": "video_file", "title": "🎬 Video", "description": "Get a sample video file"},
55
+ {"id": "audio_file", "title": "🎵 Audio", "description": "Get a sample audio file"},
56
+ {"id": "document_file", "title": "📄 Document", "description": "Get a sample document file"}
57
+ ]
58
+ }
59
+ ```
60
+
61
+ ## File Recommendations
62
+
63
+ ### For Demo/Testing:
64
+ - **Image**: Screenshots of the app, logos, or demo graphics
65
+ - **Video**: App walkthrough, feature demonstration, or intro video
66
+ - **Audio**: Welcome message, jingle, or app sounds
67
+ - **Document**: User manual, API documentation, or feature list
68
+
69
+ ### For Production:
70
+ - **Image**: Product catalogs, infographics, charts
71
+ - **Video**: Product demos, tutorials, testimonials
72
+ - **Audio**: Voice messages, audio guides, music
73
+ - **Document**: Contracts, invoices, manuals, reports
74
+
75
+ ## WhatsApp Business API Limits
76
+
77
+ - **Images**: JPEG, PNG up to 5MB, 8-bit RGB or RGBA
78
+ - **Videos**: MP4, 3GP up to 16MB, H.264 codec, AAC audio
79
+ - **Audio**: AAC, AMR, MP3, M4A, OGG up to 16MB
80
+ - **Documents**: TXT, PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX up to 100MB
81
+
82
+ ## Fallback Behavior
83
+
84
+ If files are missing:
85
+ - App will send a text message instead
86
+ - Message will indicate the selected media type
87
+ - List functionality will still work, but without actual media
88
+
89
+ ## Example Implementation
90
+
91
+ ```python
92
+ # In state_handlers.py
93
+ media_mapping = {
94
+ "image_file": ("image.png", "image"),
95
+ "video_file": ("video.mp4", "video"),
96
+ "audio_file": ("audio.mp3", "audio"),
97
+ "document_file": ("document.pdf", "document")
98
+ }
99
+
100
+ media_file, media_type = media_mapping.get(selection_id, (None, None))
101
+
102
+ if media_file:
103
+ await send_local_media_file(
104
+ messenger=self.messenger,
105
+ recipient=user_id,
106
+ filename=media_file,
107
+ media_subdir="list",
108
+ caption=f"Here's your {media_type} file! 🎉"
109
+ )
110
+ ```