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
@@ -103,10 +103,10 @@ class WappaBuilder:
103
103
  def add_startup_hook(self, hook: Callable, priority: int = 50) -> "WappaBuilder":
104
104
  """
105
105
  Add a startup hook to unified lifespan management.
106
-
106
+
107
107
  Hooks are executed during application startup in priority order.
108
108
  Lower priority numbers execute first.
109
-
109
+
110
110
  Priority Guidelines:
111
111
  - 10: Core system initialization (logging, sessions)
112
112
  - 20: Infrastructure (databases, caches, external services)
@@ -120,11 +120,11 @@ class WappaBuilder:
120
120
 
121
121
  Returns:
122
122
  Self for method chaining
123
-
123
+
124
124
  Example:
125
125
  async def my_startup(app: FastAPI):
126
126
  print("Starting my service")
127
-
127
+
128
128
  builder.add_startup_hook(my_startup, priority=30)
129
129
  """
130
130
  self.startup_hooks.append((hook, priority))
@@ -133,10 +133,10 @@ class WappaBuilder:
133
133
  def add_shutdown_hook(self, hook: Callable, priority: int = 50) -> "WappaBuilder":
134
134
  """
135
135
  Add a shutdown hook to unified lifespan management.
136
-
136
+
137
137
  Hooks are executed during application shutdown in reverse priority order.
138
138
  Higher priority numbers execute first during shutdown.
139
-
139
+
140
140
  Priority Guidelines:
141
141
  - 90: Core system cleanup (sessions, logging) - runs last
142
142
  - 70: Application services cleanup
@@ -146,16 +146,16 @@ class WappaBuilder:
146
146
  - 10: Early cleanup
147
147
 
148
148
  Args:
149
- hook: Async callable that takes (app: FastAPI) -> None
149
+ hook: Async callable that takes (app: FastAPI) -> None
150
150
  priority: Execution priority (higher = runs first in shutdown)
151
151
 
152
152
  Returns:
153
153
  Self for method chaining
154
-
154
+
155
155
  Example:
156
156
  async def my_shutdown(app: FastAPI):
157
157
  print("Cleaning up my service")
158
-
158
+
159
159
  builder.add_shutdown_hook(my_shutdown, priority=30)
160
160
  """
161
161
  self.shutdown_hooks.append((hook, priority))
@@ -183,7 +183,7 @@ class WappaBuilder:
183
183
  2. Create FastAPI app with lifespan and config
184
184
  3. Add all middleware via app.add_middleware()
185
185
  4. Include all routers via app.include_router()
186
-
186
+
187
187
  Only async operations happen in the lifespan (startup/shutdown hooks).
188
188
 
189
189
  Returns:
@@ -191,22 +191,21 @@ class WappaBuilder:
191
191
  """
192
192
  logger = get_app_logger()
193
193
  logger.debug(f"🏗️ Building FastAPI app with {len(self.plugins)} plugins")
194
-
194
+
195
195
  # Step 1: Configure plugins (sync setup only - middleware/router registration)
196
196
  if self.plugins:
197
197
  logger.debug(f"⚙️ Configuring {len(self.plugins)} plugins synchronously...")
198
198
  for plugin in self.plugins:
199
199
  plugin.configure(self) # Synchronous configuration
200
-
200
+
201
201
  logger.info(
202
202
  f"✅ Plugin configuration complete - registered {len(self.middlewares)} middlewares, "
203
203
  f"{len(self.routers)} routers, {len(self.startup_hooks)} startup hooks, "
204
204
  f"{len(self.shutdown_hooks)} shutdown hooks"
205
205
  )
206
-
206
+
207
207
  # Create unified lifespan (only for async startup/shutdown hooks)
208
- from contextlib import asynccontextmanager
209
-
208
+
210
209
  @asynccontextmanager
211
210
  async def unified_lifespan(app: FastAPI):
212
211
  try:
@@ -241,9 +240,11 @@ class WappaBuilder:
241
240
  sorted_middlewares = sorted(self.middlewares, key=lambda x: x[2], reverse=True)
242
241
  for middleware_class, kwargs, priority in sorted_middlewares:
243
242
  app.add_middleware(middleware_class, **kwargs)
244
- logger.debug(f"Added middleware {middleware_class.__name__} (priority: {priority})")
243
+ logger.debug(
244
+ f"Added middleware {middleware_class.__name__} (priority: {priority})"
245
+ )
245
246
 
246
- # Step 4: Include all routers via app.include_router()
247
+ # Step 4: Include all routers via app.include_router()
247
248
  for router, kwargs in self.routers:
248
249
  app.include_router(router, **kwargs)
249
250
  logger.debug(f"Included router with config: {kwargs}")
@@ -258,7 +259,7 @@ class WappaBuilder:
258
259
  async def _execute_all_startup_hooks(self, app: FastAPI) -> None:
259
260
  """
260
261
  Execute all startup hooks in unified priority order.
261
-
262
+
262
263
  This replaces the old plugin-specific startup execution with a unified
263
264
  approach where all hooks (from plugins and user code) are executed
264
265
  in a single priority-ordered sequence.
@@ -276,17 +277,23 @@ class WappaBuilder:
276
277
  logger.debug("No startup hooks registered")
277
278
  return
278
279
 
279
- logger.debug(f"Executing {len(sorted_hooks)} startup hooks in priority order...")
280
+ logger.debug(
281
+ f"Executing {len(sorted_hooks)} startup hooks in priority order..."
282
+ )
280
283
 
281
284
  for hook, priority in sorted_hooks:
282
285
  hook_name = getattr(hook, "__name__", "anonymous_hook")
283
- logger.debug(f"⚡ Executing startup hook: {hook_name} (priority: {priority})")
286
+ logger.debug(
287
+ f"⚡ Executing startup hook: {hook_name} (priority: {priority})"
288
+ )
284
289
 
285
290
  try:
286
291
  await hook(app)
287
292
  logger.debug(f"✅ Startup hook {hook_name} completed")
288
293
  except Exception as e:
289
- logger.error(f"❌ Startup hook {hook_name} failed: {e}", exc_info=True)
294
+ logger.error(
295
+ f"❌ Startup hook {hook_name} failed: {e}", exc_info=True
296
+ )
290
297
  raise # Re-raise to fail fast
291
298
 
292
299
  except Exception as e:
@@ -296,7 +303,7 @@ class WappaBuilder:
296
303
  async def _execute_all_shutdown_hooks(self, app: FastAPI) -> None:
297
304
  """
298
305
  Execute all shutdown hooks in reverse priority order.
299
-
306
+
300
307
  This replaces the old plugin-specific shutdown execution with a unified
301
308
  approach where all hooks are executed in reverse priority order,
302
309
  with error isolation to prevent shutdown cascading failures.
@@ -313,14 +320,20 @@ class WappaBuilder:
313
320
  logger.debug("No shutdown hooks registered")
314
321
  return
315
322
 
316
- logger.debug(f"Executing {len(sorted_hooks)} shutdown hooks in reverse priority order...")
323
+ logger.debug(
324
+ f"Executing {len(sorted_hooks)} shutdown hooks in reverse priority order..."
325
+ )
317
326
 
318
327
  for hook, priority in sorted_hooks:
319
328
  hook_name = getattr(hook, "__name__", "anonymous_hook")
320
329
  try:
321
- logger.debug(f"🛑 Executing shutdown hook: {hook_name} (priority: {priority})")
330
+ logger.debug(
331
+ f"🛑 Executing shutdown hook: {hook_name} (priority: {priority})"
332
+ )
322
333
  await hook(app)
323
334
  logger.debug(f"✅ Shutdown hook {hook_name} completed")
324
335
  except Exception as e:
325
336
  # Don't re-raise in shutdown - log and continue with other hooks
326
- logger.error(f"❌ Error in shutdown hook {hook_name}: {e}", exc_info=True)
337
+ logger.error(
338
+ f"❌ Error in shutdown hook {hook_name}: {e}", exc_info=True
339
+ )
@@ -120,9 +120,7 @@ class RedisPlugin:
120
120
  max_conn = self.max_connections or settings.redis_max_connections
121
121
 
122
122
  logger.info("=== REDIS CACHE INITIALIZATION ===")
123
- logger.info(
124
- f"🔴 Redis URL: {redis_url} (max_connections: {max_conn})"
125
- )
123
+ logger.info(f"🔴 Redis URL: {redis_url} (max_connections: {max_conn})")
126
124
 
127
125
  # Initialize Redis pools with explicit settings
128
126
  await RedisManager.initialize(
@@ -152,16 +152,17 @@ class WappaCorePlugin:
152
152
  # Create persistent HTTP session with optimized connection pooling
153
153
  logger.info("🌐 Creating persistent HTTP session...")
154
154
  connector = aiohttp.TCPConnector(
155
- limit=100, # Max connections
156
- keepalive_timeout=30, # Keep alive timeout
157
- enable_cleanup_closed=True # Auto cleanup closed connections
155
+ limit=100, # Max connections
156
+ keepalive_timeout=30, # Keep alive timeout
157
+ enable_cleanup_closed=True, # Auto cleanup closed connections
158
158
  )
159
159
  session = aiohttp.ClientSession(
160
- connector=connector,
161
- timeout=aiohttp.ClientTimeout(total=30)
160
+ connector=connector, timeout=aiohttp.ClientTimeout(total=30)
162
161
  )
163
162
  app.state.http_session = session
164
- logger.info("✅ Persistent HTTP session created - connections: 100, keepalive: 30s")
163
+ logger.info(
164
+ "✅ Persistent HTTP session created - connections: 100, keepalive: 30s"
165
+ )
165
166
 
166
167
  # Log available endpoints
167
168
  base_url = (
wappa/core/types.py CHANGED
@@ -11,7 +11,7 @@ from typing import Literal
11
11
  class CacheType(Enum):
12
12
  """
13
13
  Supported cache types for Wappa applications.
14
-
14
+
15
15
  This enum defines the cache backends that Wappa can use for state management,
16
16
  user data caching, and message logging.
17
17
  """
@@ -43,20 +43,20 @@ Example:
43
43
  def validate_cache_type(cache_type: str) -> CacheType:
44
44
  """
45
45
  Validate and convert a cache type string to CacheType enum.
46
-
46
+
47
47
  Args:
48
48
  cache_type: String representation of cache type
49
-
49
+
50
50
  Returns:
51
51
  Validated CacheType enum value
52
-
52
+
53
53
  Raises:
54
54
  ValueError: If cache_type is not supported
55
-
55
+
56
56
  Example:
57
57
  >>> validate_cache_type("redis")
58
58
  CacheType.REDIS
59
-
59
+
60
60
  >>> validate_cache_type("invalid")
61
61
  ValueError: Unsupported cache type: invalid. Supported types: memory, redis, json
62
62
  """
@@ -73,10 +73,10 @@ def validate_cache_type(cache_type: str) -> CacheType:
73
73
  def get_supported_cache_types() -> list[str]:
74
74
  """
75
75
  Get list of all supported cache type strings.
76
-
76
+
77
77
  Returns:
78
78
  List of supported cache type strings
79
-
79
+
80
80
  Example:
81
81
  >>> get_supported_cache_types()
82
82
  ['memory', 'redis', 'json']
@@ -87,17 +87,17 @@ def get_supported_cache_types() -> list[str]:
87
87
  def is_cache_type_supported(cache_type: str) -> bool:
88
88
  """
89
89
  Check if a cache type string is supported.
90
-
90
+
91
91
  Args:
92
92
  cache_type: String to check
93
-
93
+
94
94
  Returns:
95
95
  True if cache type is supported, False otherwise
96
-
96
+
97
97
  Example:
98
98
  >>> is_cache_type_supported("redis")
99
99
  True
100
-
100
+
101
101
  >>> is_cache_type_supported("invalid")
102
102
  False
103
103
  """
wappa/core/wappa_app.py CHANGED
@@ -167,7 +167,7 @@ class Wappa:
167
167
  Return a FastAPI ASGI application, building synchronously if needed.
168
168
 
169
169
  This property enables uvicorn reload compatibility by providing a synchronous
170
- way to access the FastAPI app. Plugin configuration is deferred to lifespan
170
+ way to access the FastAPI app. Plugin configuration is deferred to lifespan
171
171
  hooks to maintain async initialization while keeping this property sync.
172
172
 
173
173
  Returns:
@@ -205,7 +205,7 @@ class Wappa:
205
205
  redoc_url="/redoc" if settings.is_development else None,
206
206
  )
207
207
 
208
- # Use WappaBuilder.build() - creates app with lifespan,
208
+ # Use WappaBuilder.build() - creates app with lifespan,
209
209
  # defers plugin configuration to startup hooks
210
210
  app = self._builder.build()
211
211
 
@@ -329,10 +329,12 @@ class Wappa:
329
329
 
330
330
  return self
331
331
 
332
- def add_middleware(self, middleware_class: type, priority: int = 50, **kwargs) -> "Wappa":
332
+ def add_middleware(
333
+ self, middleware_class: type, priority: int = 50, **kwargs
334
+ ) -> "Wappa":
333
335
  """
334
336
  Add middleware to the application with priority ordering.
335
-
337
+
336
338
  This provides access to the underlying WappaBuilder's middleware system.
337
339
  Priority determines execution order:
338
340
  - Lower numbers run first (outer middleware)
@@ -349,7 +351,7 @@ class Wappa:
349
351
 
350
352
  Example:
351
353
  from fastapi.middleware.cors import CORSMiddleware
352
-
354
+
353
355
  app = Wappa(cache="redis")
354
356
  app.add_middleware(CORSMiddleware, allow_origins=["*"], priority=30)
355
357
  app.set_event_handler(MyHandler())
@@ -379,9 +381,9 @@ class Wappa:
379
381
 
380
382
  Example:
381
383
  from fastapi import APIRouter
382
-
384
+
383
385
  custom_router = APIRouter()
384
-
386
+
385
387
  app = Wappa(cache="redis")
386
388
  app.add_router(custom_router, prefix="/api/v1", tags=["custom"])
387
389
  app.set_event_handler(MyHandler())
@@ -411,7 +413,7 @@ class Wappa:
411
413
  app = Wappa(cache="redis")
412
414
  app.configure(
413
415
  title="My WhatsApp Bot",
414
- version="2.0.0",
416
+ version="2.0.0",
415
417
  description="Custom bot with advanced features"
416
418
  )
417
419
  app.set_event_handler(MyHandler())
@@ -1,7 +1,7 @@
1
1
  """
2
2
  Wappa Database Components
3
3
 
4
- Provides database abstraction and adapters for SQLModel/SQLAlchemy async
4
+ Provides database abstraction and adapters for SQLModel/SQLAlchemy async
5
5
  connections. Supports multiple database engines with a unified interface.
6
6
 
7
7
  Clean Architecture: Infrastructure layer database adapters.
@@ -9,7 +9,7 @@ Clean Architecture: Infrastructure layer database adapters.
9
9
  Usage:
10
10
  # Base adapter
11
11
  from wappa.database import DatabaseAdapter
12
-
12
+
13
13
  # Specific database adapters
14
14
  from wappa.database import PostgreSQLAdapter, MySQLAdapter, SQLiteAdapter
15
15
  """
@@ -22,9 +22,8 @@ from .adapters.sqlite_adapter import SQLiteAdapter
22
22
  __all__ = [
23
23
  # Base Adapter
24
24
  "DatabaseAdapter",
25
-
26
25
  # Database Adapters (Clean Architecture: Infrastructure implementations)
27
26
  "PostgreSQLAdapter",
28
- "MySQLAdapter",
27
+ "MySQLAdapter",
29
28
  "SQLiteAdapter",
30
29
  ]
@@ -36,8 +36,7 @@ def create_messenger_platform_enum() -> type[Enum]:
36
36
 
37
37
  # Create enum members dynamically based on enabled platforms
38
38
  enum_members = {
39
- platform_name.upper(): platform_name
40
- for platform_name in enabled_platforms.keys()
39
+ platform_name.upper(): platform_name for platform_name in enabled_platforms
41
40
  }
42
41
 
43
42
  # Create the enum class
@@ -195,10 +195,7 @@ class WhatsAppMediaFactory(MediaFactory):
195
195
  }
196
196
 
197
197
  # Create media object based on reference type
198
- if is_url:
199
- media_obj = {"link": media_reference}
200
- else:
201
- media_obj = {"id": media_reference}
198
+ media_obj = {"link": media_reference} if is_url else {"id": media_reference}
202
199
 
203
200
  # Add optional caption
204
201
  if caption:
@@ -232,10 +229,7 @@ class WhatsAppMediaFactory(MediaFactory):
232
229
  }
233
230
 
234
231
  # Create media object based on reference type
235
- if is_url:
236
- media_obj = {"link": media_reference}
237
- else:
238
- media_obj = {"id": media_reference}
232
+ media_obj = {"link": media_reference} if is_url else {"id": media_reference}
239
233
 
240
234
  # Add optional caption
241
235
  if caption:
@@ -269,10 +263,7 @@ class WhatsAppMediaFactory(MediaFactory):
269
263
  }
270
264
 
271
265
  # Create media object based on reference type
272
- if is_url:
273
- media_obj = {"link": media_reference}
274
- else:
275
- media_obj = {"id": media_reference}
266
+ media_obj = {"link": media_reference} if is_url else {"id": media_reference}
276
267
 
277
268
  payload["audio"] = media_obj
278
269
 
@@ -302,10 +293,7 @@ class WhatsAppMediaFactory(MediaFactory):
302
293
  }
303
294
 
304
295
  # Create media object based on reference type
305
- if is_url:
306
- media_obj = {"link": media_reference}
307
- else:
308
- media_obj = {"id": media_reference}
296
+ media_obj = {"link": media_reference} if is_url else {"id": media_reference}
309
297
 
310
298
  # Add optional filename
311
299
  if filename:
@@ -339,10 +327,7 @@ class WhatsAppMediaFactory(MediaFactory):
339
327
  }
340
328
 
341
329
  # Create media object based on reference type
342
- if is_url:
343
- media_obj = {"link": media_reference}
344
- else:
345
- media_obj = {"id": media_reference}
330
+ media_obj = {"link": media_reference} if is_url else {"id": media_reference}
346
331
 
347
332
  payload["sticker"] = media_obj
348
333
 
@@ -279,10 +279,7 @@ class WhatsAppMessageFactory(MessageFactory):
279
279
  }
280
280
 
281
281
  # Create media object based on reference type
282
- if is_url:
283
- media_obj = {"link": media_reference}
284
- else:
285
- media_obj = {"id": media_reference}
282
+ media_obj = {"link": media_reference} if is_url else {"id": media_reference}
286
283
 
287
284
  # Add optional caption
288
285
  if caption:
@@ -313,10 +310,7 @@ class WhatsAppMessageFactory(MessageFactory):
313
310
  }
314
311
 
315
312
  # Create media object based on reference type
316
- if is_url:
317
- media_obj = {"link": media_reference}
318
- else:
319
- media_obj = {"id": media_reference}
313
+ media_obj = {"link": media_reference} if is_url else {"id": media_reference}
320
314
 
321
315
  # Add optional caption
322
316
  if caption:
@@ -346,10 +340,7 @@ class WhatsAppMessageFactory(MessageFactory):
346
340
  }
347
341
 
348
342
  # Create media object based on reference type
349
- if is_url:
350
- media_obj = {"link": media_reference}
351
- else:
352
- media_obj = {"id": media_reference}
343
+ media_obj = {"link": media_reference} if is_url else {"id": media_reference}
353
344
 
354
345
  payload["audio"] = media_obj
355
346
 
@@ -376,10 +367,7 @@ class WhatsAppMessageFactory(MessageFactory):
376
367
  }
377
368
 
378
369
  # Create media object based on reference type
379
- if is_url:
380
- media_obj = {"link": media_reference}
381
- else:
382
- media_obj = {"id": media_reference}
370
+ media_obj = {"link": media_reference} if is_url else {"id": media_reference}
383
371
 
384
372
  # Add optional filename
385
373
  if filename:
@@ -409,10 +397,7 @@ class WhatsAppMessageFactory(MessageFactory):
409
397
  }
410
398
 
411
399
  # Create media object based on reference type
412
- if is_url:
413
- media_obj = {"link": media_reference}
414
- else:
415
- media_obj = {"id": media_reference}
400
+ media_obj = {"link": media_reference} if is_url else {"id": media_reference}
416
401
 
417
402
  payload["sticker"] = media_obj
418
403
 
@@ -210,7 +210,7 @@ class MessengerFactory:
210
210
  # Clear all messengers for a platform
211
211
  to_remove = [
212
212
  key
213
- for key in self._messenger_cache.keys()
213
+ for key in self._messenger_cache
214
214
  if key.startswith(f"{platform.value}:")
215
215
  ]
216
216
  for key in to_remove:
@@ -219,9 +219,7 @@ class MessengerFactory:
219
219
  elif tenant_id:
220
220
  # Clear all messengers for a tenant
221
221
  to_remove = [
222
- key
223
- for key in self._messenger_cache.keys()
224
- if key.endswith(f":{tenant_id}")
222
+ key for key in self._messenger_cache if key.endswith(f":{tenant_id}")
225
223
  ]
226
224
  for key in to_remove:
227
225
  del self._messenger_cache[key]
@@ -169,31 +169,31 @@ class ICache(ABC):
169
169
  True if successful, False otherwise
170
170
  """
171
171
  pass
172
-
172
+
173
173
  @staticmethod
174
174
  def create_table_key(table_name: str, pkid: str) -> str:
175
175
  """
176
176
  Create a properly formatted table cache key.
177
-
177
+
178
178
  This is a static utility method available on all cache implementations
179
179
  to ensure consistent key formatting across cache backends.
180
-
180
+
181
181
  Args:
182
182
  table_name: Name of the table (e.g., "user_profiles", "message_logs")
183
183
  pkid: Primary key ID (e.g., user_id, message_id)
184
-
184
+
185
185
  Returns:
186
186
  Formatted key string for use with cache methods
187
-
187
+
188
188
  Example:
189
189
  key = ICache.create_table_key("user_profiles", "12345")
190
190
  # Returns: "user_profiles:12345"
191
191
  """
192
192
  if not table_name or not pkid:
193
193
  raise ValueError("Both table_name and pkid must be provided and non-empty")
194
-
194
+
195
195
  # Sanitize inputs to avoid conflicts
196
196
  safe_table_name = str(table_name).replace(":", "_")
197
197
  safe_pkid = str(pkid).replace(":", "_")
198
-
198
+
199
199
  return f"{safe_table_name}:{safe_pkid}"
@@ -14,9 +14,8 @@ and WhatsApp Cloud API 2025 specifications for the 4 core endpoints:
14
14
 
15
15
  from abc import ABC, abstractmethod
16
16
  from collections.abc import AsyncIterator
17
- from contextlib import asynccontextmanager
18
17
  from pathlib import Path
19
- from typing import BinaryIO, AsyncContextManager
18
+ from typing import AsyncContextManager, BinaryIO
20
19
 
21
20
  from wappa.domain.models.media_result import (
22
21
  MediaDeleteResult,
@@ -257,9 +256,7 @@ class IMediaHandler(ABC):
257
256
  pass
258
257
 
259
258
  @abstractmethod
260
- async def get_media_as_bytes(
261
- self, media_id: str
262
- ) -> MediaDownloadResult:
259
+ async def get_media_as_bytes(self, media_id: str) -> MediaDownloadResult:
263
260
  """
264
261
  Download media as bytes without creating any files.
265
262
 
@@ -6,10 +6,8 @@ messaging platforms, providing consistent response structures while
6
6
  maintaining compatibility with platform-specific response formats.
7
7
  """
8
8
 
9
- import os
10
9
  from datetime import datetime
11
10
  from pathlib import Path
12
- from typing import AsyncContextManager, Optional
13
11
 
14
12
  from pydantic import BaseModel, Field
15
13
 
@@ -125,7 +123,7 @@ class MediaDownloadResult(BaseModel):
125
123
 
126
124
  def mark_as_temp_file(self, cleanup_on_exit: bool = True):
127
125
  """Mark this result as containing a temporary file for cleanup.
128
-
126
+
129
127
  Args:
130
128
  cleanup_on_exit: Whether to automatically delete the temp file when context exits
131
129
  """
@@ -51,9 +51,7 @@ class PlatformCredentials(BaseModel):
51
51
  def is_configured(self) -> bool:
52
52
  """Check if minimum required credentials are present."""
53
53
  # For WhatsApp, we need access_token and phone_id
54
- if self.access_token and self.phone_id:
55
- return True
56
- return False
54
+ return bool(self.access_token and self.phone_id)
57
55
 
58
56
 
59
57
  class PlatformLimits(BaseModel):
@@ -9,11 +9,11 @@ Clean Architecture: Application services and infrastructure implementations.
9
9
  Usage (User Request: Quick access to WhatsApp messaging components):
10
10
  # Core messaging interface
11
11
  from wappa.messaging import IMessenger
12
-
12
+
13
13
  # WhatsApp client and messenger
14
14
  from wappa.messaging.whatsapp import WhatsAppClient, WhatsAppMessenger
15
-
16
- # WhatsApp specialized handlers
15
+
16
+ # WhatsApp specialized handlers
17
17
  from wappa.messaging.whatsapp import (
18
18
  WhatsAppMediaHandler,
19
19
  WhatsAppInteractiveHandler,
@@ -26,32 +26,29 @@ Usage (User Request: Quick access to WhatsApp messaging components):
26
26
  from wappa.domain.interfaces.messaging_interface import IMessenger
27
27
 
28
28
  # WhatsApp Client & Messenger (User Request: Quick access)
29
- from .whatsapp.client import WhatsAppClient, WhatsAppUrlBuilder, WhatsAppFormDataBuilder
30
- from .whatsapp.messenger import WhatsAppMessenger
29
+ from .whatsapp.client import WhatsAppClient, WhatsAppFormDataBuilder, WhatsAppUrlBuilder
31
30
 
32
31
  # WhatsApp Specialized Handlers (User Request: Quick access)
33
32
  from .whatsapp.handlers import (
33
+ WhatsAppInteractiveHandler,
34
34
  WhatsAppMediaHandler,
35
- WhatsAppInteractiveHandler,
36
- WhatsAppTemplateHandler,
37
35
  WhatsAppSpecializedHandler,
36
+ WhatsAppTemplateHandler,
38
37
  )
38
+ from .whatsapp.messenger import WhatsAppMessenger
39
39
 
40
40
  __all__ = [
41
41
  # Core Interface
42
42
  "IMessenger",
43
-
44
43
  # WhatsApp Client & Utilities
45
44
  "WhatsAppClient",
46
45
  "WhatsAppUrlBuilder",
47
- "WhatsAppFormDataBuilder",
48
-
46
+ "WhatsAppFormDataBuilder",
49
47
  # WhatsApp Messenger
50
48
  "WhatsAppMessenger",
51
-
52
49
  # WhatsApp Handlers (User Request: Clean access to all handlers)
53
50
  "WhatsAppMediaHandler",
54
51
  "WhatsAppInteractiveHandler",
55
- "WhatsAppTemplateHandler",
52
+ "WhatsAppTemplateHandler",
56
53
  "WhatsAppSpecializedHandler",
57
54
  ]