django-nativemojo 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.
Files changed (194) hide show
  1. django_nativemojo-0.1.10.dist-info/LICENSE +19 -0
  2. django_nativemojo-0.1.10.dist-info/METADATA +96 -0
  3. django_nativemojo-0.1.10.dist-info/NOTICE +8 -0
  4. django_nativemojo-0.1.10.dist-info/RECORD +194 -0
  5. django_nativemojo-0.1.10.dist-info/WHEEL +4 -0
  6. mojo/__init__.py +3 -0
  7. mojo/apps/account/__init__.py +1 -0
  8. mojo/apps/account/admin.py +91 -0
  9. mojo/apps/account/apps.py +16 -0
  10. mojo/apps/account/migrations/0001_initial.py +77 -0
  11. mojo/apps/account/migrations/0002_user_is_email_verified_user_is_phone_verified.py +23 -0
  12. mojo/apps/account/migrations/0003_group_mojo_secrets_user_mojo_secrets.py +23 -0
  13. mojo/apps/account/migrations/__init__.py +0 -0
  14. mojo/apps/account/models/__init__.py +3 -0
  15. mojo/apps/account/models/group.py +98 -0
  16. mojo/apps/account/models/member.py +95 -0
  17. mojo/apps/account/models/pkey.py +18 -0
  18. mojo/apps/account/models/user.py +211 -0
  19. mojo/apps/account/rest/__init__.py +3 -0
  20. mojo/apps/account/rest/group.py +25 -0
  21. mojo/apps/account/rest/user.py +47 -0
  22. mojo/apps/account/utils/__init__.py +0 -0
  23. mojo/apps/account/utils/jwtoken.py +72 -0
  24. mojo/apps/account/utils/passkeys.py +54 -0
  25. mojo/apps/fileman/README.md +549 -0
  26. mojo/apps/fileman/__init__.py +0 -0
  27. mojo/apps/fileman/apps.py +15 -0
  28. mojo/apps/fileman/backends/__init__.py +117 -0
  29. mojo/apps/fileman/backends/base.py +319 -0
  30. mojo/apps/fileman/backends/filesystem.py +397 -0
  31. mojo/apps/fileman/backends/s3.py +398 -0
  32. mojo/apps/fileman/examples/configurations.py +378 -0
  33. mojo/apps/fileman/examples/usage_example.py +665 -0
  34. mojo/apps/fileman/management/__init__.py +1 -0
  35. mojo/apps/fileman/management/commands/__init__.py +1 -0
  36. mojo/apps/fileman/management/commands/cleanup_expired_uploads.py +222 -0
  37. mojo/apps/fileman/models/__init__.py +7 -0
  38. mojo/apps/fileman/models/file.py +292 -0
  39. mojo/apps/fileman/models/manager.py +227 -0
  40. mojo/apps/fileman/models/render.py +0 -0
  41. mojo/apps/fileman/rest/__init__ +0 -0
  42. mojo/apps/fileman/rest/__init__.py +23 -0
  43. mojo/apps/fileman/rest/fileman.py +13 -0
  44. mojo/apps/fileman/rest/upload.py +92 -0
  45. mojo/apps/fileman/utils/__init__.py +19 -0
  46. mojo/apps/fileman/utils/upload.py +616 -0
  47. mojo/apps/incident/__init__.py +1 -0
  48. mojo/apps/incident/handlers/__init__.py +3 -0
  49. mojo/apps/incident/handlers/event_handlers.py +142 -0
  50. mojo/apps/incident/migrations/0001_initial.py +83 -0
  51. mojo/apps/incident/migrations/0002_rename_bundle_ruleset_bundle_minutes_event_hostname_and_more.py +44 -0
  52. mojo/apps/incident/migrations/0003_alter_event_model_id.py +18 -0
  53. mojo/apps/incident/migrations/0004_alter_incident_model_id.py +18 -0
  54. mojo/apps/incident/migrations/__init__.py +0 -0
  55. mojo/apps/incident/models/__init__.py +3 -0
  56. mojo/apps/incident/models/event.py +135 -0
  57. mojo/apps/incident/models/incident.py +33 -0
  58. mojo/apps/incident/models/rule.py +247 -0
  59. mojo/apps/incident/parsers/__init__.py +0 -0
  60. mojo/apps/incident/parsers/ossec/__init__.py +1 -0
  61. mojo/apps/incident/parsers/ossec/core.py +82 -0
  62. mojo/apps/incident/parsers/ossec/parsed.py +23 -0
  63. mojo/apps/incident/parsers/ossec/rules.py +124 -0
  64. mojo/apps/incident/parsers/ossec/utils.py +169 -0
  65. mojo/apps/incident/reporter.py +42 -0
  66. mojo/apps/incident/rest/__init__.py +2 -0
  67. mojo/apps/incident/rest/event.py +23 -0
  68. mojo/apps/incident/rest/ossec.py +22 -0
  69. mojo/apps/logit/__init__.py +0 -0
  70. mojo/apps/logit/admin.py +37 -0
  71. mojo/apps/logit/migrations/0001_initial.py +32 -0
  72. mojo/apps/logit/migrations/0002_log_duid_log_payload_log_username.py +28 -0
  73. mojo/apps/logit/migrations/0003_log_level.py +18 -0
  74. mojo/apps/logit/migrations/__init__.py +0 -0
  75. mojo/apps/logit/models/__init__.py +1 -0
  76. mojo/apps/logit/models/log.py +57 -0
  77. mojo/apps/logit/rest.py +9 -0
  78. mojo/apps/metrics/README.md +79 -0
  79. mojo/apps/metrics/__init__.py +12 -0
  80. mojo/apps/metrics/redis_metrics.py +331 -0
  81. mojo/apps/metrics/rest/__init__.py +1 -0
  82. mojo/apps/metrics/rest/base.py +152 -0
  83. mojo/apps/metrics/rest/db.py +0 -0
  84. mojo/apps/metrics/utils.py +227 -0
  85. mojo/apps/notify/README.md +91 -0
  86. mojo/apps/notify/README_NOTIFICATIONS.md +566 -0
  87. mojo/apps/notify/__init__.py +0 -0
  88. mojo/apps/notify/admin.py +52 -0
  89. mojo/apps/notify/handlers/__init__.py +0 -0
  90. mojo/apps/notify/handlers/example_handlers.py +516 -0
  91. mojo/apps/notify/handlers/ses/__init__.py +25 -0
  92. mojo/apps/notify/handlers/ses/bounce.py +0 -0
  93. mojo/apps/notify/handlers/ses/complaint.py +25 -0
  94. mojo/apps/notify/handlers/ses/message.py +86 -0
  95. mojo/apps/notify/management/__init__.py +0 -0
  96. mojo/apps/notify/management/commands/__init__.py +1 -0
  97. mojo/apps/notify/management/commands/process_notifications.py +370 -0
  98. mojo/apps/notify/mod +0 -0
  99. mojo/apps/notify/models/__init__.py +12 -0
  100. mojo/apps/notify/models/account.py +128 -0
  101. mojo/apps/notify/models/attachment.py +24 -0
  102. mojo/apps/notify/models/bounce.py +68 -0
  103. mojo/apps/notify/models/complaint.py +40 -0
  104. mojo/apps/notify/models/inbox.py +113 -0
  105. mojo/apps/notify/models/inbox_message.py +173 -0
  106. mojo/apps/notify/models/outbox.py +129 -0
  107. mojo/apps/notify/models/outbox_message.py +288 -0
  108. mojo/apps/notify/models/template.py +30 -0
  109. mojo/apps/notify/providers/__init__.py +0 -0
  110. mojo/apps/notify/providers/aws.py +73 -0
  111. mojo/apps/notify/rest/__init__.py +0 -0
  112. mojo/apps/notify/rest/ses.py +0 -0
  113. mojo/apps/notify/utils/__init__.py +2 -0
  114. mojo/apps/notify/utils/notifications.py +404 -0
  115. mojo/apps/notify/utils/parsing.py +202 -0
  116. mojo/apps/notify/utils/render.py +144 -0
  117. mojo/apps/tasks/README.md +118 -0
  118. mojo/apps/tasks/__init__.py +11 -0
  119. mojo/apps/tasks/manager.py +489 -0
  120. mojo/apps/tasks/rest/__init__.py +2 -0
  121. mojo/apps/tasks/rest/hooks.py +0 -0
  122. mojo/apps/tasks/rest/tasks.py +62 -0
  123. mojo/apps/tasks/runner.py +174 -0
  124. mojo/apps/tasks/tq_handlers.py +14 -0
  125. mojo/decorators/__init__.py +3 -0
  126. mojo/decorators/auth.py +25 -0
  127. mojo/decorators/cron.py +31 -0
  128. mojo/decorators/http.py +132 -0
  129. mojo/decorators/validate.py +14 -0
  130. mojo/errors.py +88 -0
  131. mojo/helpers/__init__.py +0 -0
  132. mojo/helpers/aws/__init__.py +0 -0
  133. mojo/helpers/aws/client.py +8 -0
  134. mojo/helpers/aws/s3.py +268 -0
  135. mojo/helpers/aws/setup_email.py +0 -0
  136. mojo/helpers/cron.py +79 -0
  137. mojo/helpers/crypto/__init__.py +4 -0
  138. mojo/helpers/crypto/aes.py +60 -0
  139. mojo/helpers/crypto/hash.py +59 -0
  140. mojo/helpers/crypto/privpub/__init__.py +1 -0
  141. mojo/helpers/crypto/privpub/hybrid.py +97 -0
  142. mojo/helpers/crypto/privpub/rsa.py +104 -0
  143. mojo/helpers/crypto/sign.py +36 -0
  144. mojo/helpers/crypto/too.l.py +25 -0
  145. mojo/helpers/crypto/utils.py +26 -0
  146. mojo/helpers/daemon.py +94 -0
  147. mojo/helpers/dates.py +69 -0
  148. mojo/helpers/dns/__init__.py +0 -0
  149. mojo/helpers/dns/godaddy.py +62 -0
  150. mojo/helpers/filetypes.py +128 -0
  151. mojo/helpers/logit.py +310 -0
  152. mojo/helpers/modules.py +95 -0
  153. mojo/helpers/paths.py +63 -0
  154. mojo/helpers/redis.py +10 -0
  155. mojo/helpers/request.py +89 -0
  156. mojo/helpers/request_parser.py +269 -0
  157. mojo/helpers/response.py +14 -0
  158. mojo/helpers/settings.py +146 -0
  159. mojo/helpers/sysinfo.py +140 -0
  160. mojo/helpers/ua.py +0 -0
  161. mojo/middleware/__init__.py +0 -0
  162. mojo/middleware/auth.py +26 -0
  163. mojo/middleware/logging.py +55 -0
  164. mojo/middleware/mojo.py +21 -0
  165. mojo/migrations/0001_initial.py +32 -0
  166. mojo/migrations/__init__.py +0 -0
  167. mojo/models/__init__.py +2 -0
  168. mojo/models/meta.py +262 -0
  169. mojo/models/rest.py +538 -0
  170. mojo/models/secrets.py +59 -0
  171. mojo/rest/__init__.py +1 -0
  172. mojo/rest/info.py +26 -0
  173. mojo/serializers/__init__.py +0 -0
  174. mojo/serializers/models.py +165 -0
  175. mojo/serializers/openapi.py +188 -0
  176. mojo/urls.py +38 -0
  177. mojo/ws4redis/README.md +174 -0
  178. mojo/ws4redis/__init__.py +2 -0
  179. mojo/ws4redis/client.py +283 -0
  180. mojo/ws4redis/connection.py +327 -0
  181. mojo/ws4redis/exceptions.py +32 -0
  182. mojo/ws4redis/redis.py +183 -0
  183. mojo/ws4redis/servers/__init__.py +0 -0
  184. mojo/ws4redis/servers/base.py +86 -0
  185. mojo/ws4redis/servers/django.py +171 -0
  186. mojo/ws4redis/servers/uwsgi.py +63 -0
  187. mojo/ws4redis/settings.py +45 -0
  188. mojo/ws4redis/utf8validator.py +128 -0
  189. mojo/ws4redis/websocket.py +403 -0
  190. testit/__init__.py +0 -0
  191. testit/client.py +147 -0
  192. testit/faker.py +20 -0
  193. testit/helpers.py +198 -0
  194. testit/runner.py +262 -0
@@ -0,0 +1,566 @@
1
+ # Django Notification System
2
+
3
+ A comprehensive multi-channel notification system for Django that supports email, SMS, WhatsApp, push notifications, and more.
4
+
5
+ ## Overview
6
+
7
+ This notification system provides a unified approach to handling both incoming and outgoing messages across multiple communication channels. It's designed to be flexible, scalable, and easy to integrate with existing Django applications.
8
+
9
+ ## Architecture
10
+
11
+ The system consists of several key components:
12
+
13
+ - **Account**: Represents a notification service account (email domain, SMS provider, etc.)
14
+ - **Inbox**: Receives messages from external sources
15
+ - **InboxMessage**: Stores received messages
16
+ - **Outbox**: Sends messages to external recipients
17
+ - **OutboxMessage**: Stores outgoing messages with status tracking
18
+
19
+ ## Models
20
+
21
+ ### Account
22
+
23
+ Represents a notification service account that can send/receive messages.
24
+
25
+ ```python
26
+ from mojo.apps.notify.models import Account
27
+
28
+ # Create an email account
29
+ email_account = Account.objects.create(
30
+ kind=Account.EMAIL,
31
+ domain='example.com',
32
+ group=my_group,
33
+ settings={
34
+ 'smtp': {
35
+ 'host': 'smtp.example.com',
36
+ 'port': 587,
37
+ 'username': 'user@example.com',
38
+ 'password': 'password',
39
+ 'use_tls': True
40
+ }
41
+ }
42
+ )
43
+
44
+ # Create an SMS account
45
+ sms_account = Account.objects.create(
46
+ kind=Account.SMS,
47
+ domain='+1234567890',
48
+ group=my_group,
49
+ settings={
50
+ 'sms_provider': {
51
+ 'provider': 'twilio',
52
+ 'account_sid': 'your_sid',
53
+ 'auth_token': 'your_token'
54
+ }
55
+ }
56
+ )
57
+ ```
58
+
59
+ ### Inbox
60
+
61
+ Defines an inbox for receiving messages.
62
+
63
+ ```python
64
+ from mojo.apps.notify.models import Inbox
65
+
66
+ # Create an email inbox
67
+ email_inbox = Inbox.objects.create(
68
+ account=email_account,
69
+ address='support@example.com',
70
+ sync_handler='myapp.handlers.on_email_received',
71
+ async_handler='myapp.handlers.on_email_received_async'
72
+ )
73
+
74
+ # Create an SMS inbox
75
+ sms_inbox = Inbox.objects.create(
76
+ account=sms_account,
77
+ address='+1234567890',
78
+ sync_handler='myapp.handlers.on_sms_received'
79
+ )
80
+ ```
81
+
82
+ ### Outbox
83
+
84
+ Defines an outbox for sending messages.
85
+
86
+ ```python
87
+ from mojo.apps.notify.models import Outbox
88
+
89
+ # Create an email outbox
90
+ email_outbox = Outbox.objects.create(
91
+ account=email_account,
92
+ group=my_group,
93
+ address='noreply@example.com',
94
+ handler='myapp.handlers.on_email_send',
95
+ rate_limit=1000 # Max 1000 emails per hour
96
+ )
97
+
98
+ # Create an SMS outbox
99
+ sms_outbox = Outbox.objects.create(
100
+ account=sms_account,
101
+ group=my_group,
102
+ address='+1234567890',
103
+ handler='myapp.handlers.on_sms_send',
104
+ rate_limit=100 # Max 100 SMS per hour
105
+ )
106
+ ```
107
+
108
+ ## Usage Examples
109
+
110
+ ### Sending Messages
111
+
112
+ ```python
113
+ from mojo.apps.notify.utils.notifications import send_email, send_sms, send_whatsapp, send_push
114
+
115
+ # Send an email
116
+ email_message = send_email(
117
+ to_address='user@example.com',
118
+ subject='Welcome!',
119
+ message='Welcome to our platform!',
120
+ from_address='welcome@example.com',
121
+ user=user_instance,
122
+ metadata={'campaign': 'welcome_series'}
123
+ )
124
+
125
+ # Send an SMS
126
+ sms_message = send_sms(
127
+ to_address='+1987654321',
128
+ message='Your verification code is: 123456',
129
+ user=user_instance
130
+ )
131
+
132
+ # Send a WhatsApp message
133
+ whatsapp_message = send_whatsapp(
134
+ to_address='+1987654321',
135
+ message='Hello from WhatsApp!',
136
+ metadata={'template_id': 'greeting'}
137
+ )
138
+
139
+ # Send a push notification
140
+ push_message = send_push(
141
+ to_address='device_token_here',
142
+ title='New Message',
143
+ message='You have a new message!',
144
+ user=user_instance,
145
+ metadata={'badge_count': 5}
146
+ )
147
+
148
+ # Schedule a message for later
149
+ from django.utils import timezone
150
+ from datetime import timedelta
151
+
152
+ scheduled_message = send_email(
153
+ to_address='user@example.com',
154
+ subject='Reminder',
155
+ message='Don\'t forget about your appointment tomorrow!',
156
+ scheduled_at=timezone.now() + timedelta(hours=24)
157
+ )
158
+ ```
159
+
160
+ ### Bulk Messaging
161
+
162
+ ```python
163
+ from mojo.apps.notify.utils.notifications import BulkNotifier
164
+
165
+ # Send bulk emails
166
+ recipients = ['user1@example.com', 'user2@example.com', 'user3@example.com']
167
+ messages = BulkNotifier.send_bulk_email(
168
+ recipients=recipients,
169
+ subject='Newsletter',
170
+ message='Check out our latest updates!',
171
+ batch_size=50
172
+ )
173
+
174
+ # Send bulk SMS
175
+ phone_numbers = ['+1111111111', '+2222222222', '+3333333333']
176
+ sms_messages = BulkNotifier.send_bulk_sms(
177
+ recipients=phone_numbers,
178
+ message='Flash sale - 50% off everything!',
179
+ batch_size=25
180
+ )
181
+ ```
182
+
183
+ ### Processing Received Messages
184
+
185
+ Messages are automatically processed when received if handlers are configured. You can also manually process them:
186
+
187
+ ```python
188
+ from mojo.apps.notify.utils.notifications import MessageProcessor
189
+
190
+ # Process a single message
191
+ success = MessageProcessor.process_inbox_message(inbox_message)
192
+
193
+ # Bulk process messages for an inbox
194
+ processed_count = MessageProcessor.bulk_process_inbox_messages(
195
+ inbox=my_inbox,
196
+ limit=100
197
+ )
198
+ ```
199
+
200
+ ## Message Handlers
201
+
202
+ ### Creating Handlers
203
+
204
+ Handlers are Python functions that process incoming or outgoing messages. Here are examples:
205
+
206
+ #### Inbox Handler (Receiving Messages)
207
+
208
+ ```python
209
+ # myapp/handlers.py
210
+ import logging
211
+ from mojo.apps.notify.models import InboxMessage
212
+
213
+ logger = logging.getLogger(__name__)
214
+
215
+ def on_email_received(inbox_message: InboxMessage) -> bool:
216
+ """Handle received email messages"""
217
+ try:
218
+ # Extract message details
219
+ sender = inbox_message.from_address
220
+ subject = inbox_message.subject
221
+ message = inbox_message.message
222
+
223
+ # Process the message
224
+ if 'support' in inbox_message.to_address:
225
+ # Create support ticket
226
+ create_support_ticket(sender, subject, message)
227
+ elif 'billing' in inbox_message.to_address:
228
+ # Handle billing inquiry
229
+ handle_billing_inquiry(inbox_message)
230
+
231
+ # Send auto-reply if needed
232
+ if should_send_auto_reply(inbox_message):
233
+ send_auto_reply(inbox_message)
234
+
235
+ logger.info(f"Processed email from {sender}")
236
+ return True
237
+
238
+ except Exception as e:
239
+ logger.error(f"Error processing email: {e}")
240
+ return False
241
+
242
+ async def on_email_received_async(inbox_message: InboxMessage) -> bool:
243
+ """Async version for high-volume processing"""
244
+ # Implement async processing logic
245
+ return True
246
+ ```
247
+
248
+ #### Outbox Handler (Sending Messages)
249
+
250
+ ```python
251
+ def on_email_send(outbox_message: OutboxMessage) -> bool:
252
+ """Handle sending email messages"""
253
+ try:
254
+ # Mark as sending
255
+ outbox_message.mark_sending()
256
+
257
+ # Get email settings
258
+ account = outbox_message.account
259
+ smtp_settings = account.get_setting('smtp', {})
260
+
261
+ # Send the email
262
+ send_via_smtp(outbox_message, smtp_settings)
263
+
264
+ # Mark as sent
265
+ outbox_message.mark_sent(message_id='external_message_id')
266
+ return True
267
+
268
+ except Exception as e:
269
+ # Mark as failed
270
+ outbox_message.mark_failed(str(e))
271
+ return False
272
+ ```
273
+
274
+ ### Handler Configuration
275
+
276
+ Configure handlers in your models:
277
+
278
+ ```python
279
+ # In your inbox
280
+ inbox.sync_handler = 'myapp.handlers.on_email_received'
281
+ inbox.async_handler = 'myapp.handlers.on_email_received_async'
282
+
283
+ # In your outbox
284
+ outbox.handler = 'myapp.handlers.on_email_send'
285
+ ```
286
+
287
+ ## Management Commands
288
+
289
+ ### Process Notifications
290
+
291
+ Use the management command to process messages:
292
+
293
+ ```bash
294
+ # Process all pending messages once
295
+ python manage.py process_notifications
296
+
297
+ # Run as daemon (continuous processing)
298
+ python manage.py process_notifications --daemon --interval=30
299
+
300
+ # Process only inbox messages
301
+ python manage.py process_notifications --inbox-only
302
+
303
+ # Process only outbox messages
304
+ python manage.py process_notifications --outbox-only
305
+
306
+ # Process only email messages
307
+ python manage.py process_notifications --kind=email
308
+
309
+ # Retry failed messages
310
+ python manage.py process_notifications --retry-failed --max-age-hours=12
311
+
312
+ # Limit batch size
313
+ python manage.py process_notifications --limit=50
314
+ ```
315
+
316
+ ### Production Deployment
317
+
318
+ For production, run the processor as a daemon:
319
+
320
+ ```bash
321
+ # Using systemd or supervisor
322
+ python manage.py process_notifications --daemon --interval=10
323
+ ```
324
+
325
+ ## Message Status Tracking
326
+
327
+ ### Outbox Message Status
328
+
329
+ - `PENDING`: Message is queued for sending
330
+ - `SENDING`: Message is currently being sent
331
+ - `SENT`: Message was successfully sent
332
+ - `FAILED`: Message failed to send
333
+ - `CANCELLED`: Message was cancelled
334
+
335
+ ### Status Checking
336
+
337
+ ```python
338
+ # Check message status
339
+ if outbox_message.is_sent:
340
+ print("Message delivered successfully")
341
+ elif outbox_message.is_failed:
342
+ print(f"Message failed: {outbox_message.error_message}")
343
+ if outbox_message.can_retry:
344
+ print("Message can be retried")
345
+
346
+ # Retry failed messages
347
+ if outbox_message.can_retry:
348
+ outbox_message.reset_for_retry()
349
+ ```
350
+
351
+ ## Rate Limiting
352
+
353
+ Configure rate limits on outboxes:
354
+
355
+ ```python
356
+ # Limit to 1000 messages per hour
357
+ outbox.rate_limit = 1000
358
+ outbox.save()
359
+
360
+ # Check rate limit
361
+ if outbox.check_rate_limit():
362
+ # Safe to send
363
+ pass
364
+ else:
365
+ # Rate limit exceeded
366
+ pass
367
+ ```
368
+
369
+ ## Message Metadata
370
+
371
+ Use metadata to store additional information:
372
+
373
+ ```python
374
+ # Email with attachments info
375
+ email_message = send_email(
376
+ to_address='user@example.com',
377
+ subject='Invoice',
378
+ message='Please find your invoice attached.',
379
+ metadata={
380
+ 'attachments': ['invoice.pdf'],
381
+ 'category': 'billing',
382
+ 'priority': 'high'
383
+ }
384
+ )
385
+
386
+ # SMS with delivery tracking
387
+ sms_message = send_sms(
388
+ to_address='+1234567890',
389
+ message='Your order has shipped!',
390
+ metadata={
391
+ 'order_id': '12345',
392
+ 'tracking_url': 'https://track.example.com/12345'
393
+ }
394
+ )
395
+
396
+ # Access metadata
397
+ attachment_info = email_message.get_metadata_value('attachments')
398
+ ```
399
+
400
+ ## Statistics and Monitoring
401
+
402
+ ```python
403
+ from mojo.apps.notify.utils.notifications import MessageStats
404
+
405
+ # Get outbox statistics
406
+ stats = MessageStats.get_outbox_stats(my_outbox)
407
+ print(f"Total sent: {stats['sent_messages']}")
408
+ print(f"Failed: {stats['failed_messages']}")
409
+ print(f"Recent (24h): {stats['recent_messages']}")
410
+
411
+ # Get inbox statistics
412
+ inbox_stats = MessageStats.get_inbox_stats(my_inbox)
413
+ print(f"Unprocessed: {inbox_stats['unprocessed_messages']}")
414
+ ```
415
+
416
+ ## Integration Examples
417
+
418
+ ### With Django Signals
419
+
420
+ ```python
421
+ from django.db.models.signals import post_save
422
+ from django.dispatch import receiver
423
+ from django.contrib.auth.models import User
424
+ from mojo.apps.notify.utils.notifications import send_email
425
+
426
+ @receiver(post_save, sender=User)
427
+ def send_welcome_email(sender, instance, created, **kwargs):
428
+ if created:
429
+ send_email(
430
+ to_address=instance.email,
431
+ subject='Welcome!',
432
+ message=f'Welcome to our platform, {instance.first_name}!',
433
+ user=instance,
434
+ metadata={'trigger': 'user_registration'}
435
+ )
436
+ ```
437
+
438
+ ### With Celery (Async)
439
+
440
+ ```python
441
+ from celery import shared_task
442
+ from mojo.apps.notify.utils.notifications import send_email
443
+
444
+ @shared_task
445
+ def send_notification_email(user_id, subject, message):
446
+ from django.contrib.auth.models import User
447
+ user = User.objects.get(id=user_id)
448
+
449
+ return send_email(
450
+ to_address=user.email,
451
+ subject=subject,
452
+ message=message,
453
+ user=user
454
+ )
455
+ ```
456
+
457
+ ### With REST API
458
+
459
+ ```python
460
+ # views.py
461
+ from rest_framework.decorators import api_view
462
+ from rest_framework.response import Response
463
+ from mojo.apps.notify.utils.notifications import send_sms
464
+
465
+ @api_view(['POST'])
466
+ def send_sms_notification(request):
467
+ phone = request.data.get('phone')
468
+ message = request.data.get('message')
469
+
470
+ sms_message = send_sms(
471
+ to_address=phone,
472
+ message=message,
473
+ user=request.user
474
+ )
475
+
476
+ return Response({
477
+ 'message_id': sms_message.id,
478
+ 'status': sms_message.status
479
+ })
480
+ ```
481
+
482
+ ## Configuration
483
+
484
+ ### Settings
485
+
486
+ Add to your Django settings:
487
+
488
+ ```python
489
+ # settings.py
490
+
491
+ # Notification system settings
492
+ NOTIFICATION_SETTINGS = {
493
+ 'DEFAULT_FROM_EMAIL': 'noreply@example.com',
494
+ 'DEFAULT_SMS_FROM': '+1234567890',
495
+ 'RATE_LIMIT_WINDOW': 3600, # 1 hour in seconds
496
+ 'MAX_RETRY_ATTEMPTS': 3,
497
+ 'PROCESSING_BATCH_SIZE': 100,
498
+ }
499
+
500
+ # Add to installed apps
501
+ INSTALLED_APPS = [
502
+ # ... other apps
503
+ 'mojo.apps.notify',
504
+ ]
505
+ ```
506
+
507
+ ### Database Migration
508
+
509
+ Run migrations to create the notification tables:
510
+
511
+ ```bash
512
+ python manage.py makemigrations notify
513
+ python manage.py migrate notify
514
+ ```
515
+
516
+ ## Admin Interface
517
+
518
+ The system includes Django admin integration for managing accounts, inboxes, outboxes, and messages. Access it at `/admin/notify/`.
519
+
520
+ ## Security Considerations
521
+
522
+ 1. **API Keys**: Store sensitive settings like API keys in environment variables
523
+ 2. **Rate Limiting**: Configure appropriate rate limits to prevent abuse
524
+ 3. **Validation**: Validate all input addresses and content
525
+ 4. **Permissions**: Use Django's permission system to control access
526
+ 5. **Logging**: Log all message activities for audit trails
527
+
528
+ ## Troubleshooting
529
+
530
+ ### Common Issues
531
+
532
+ 1. **Handler Import Errors**: Ensure handler functions are properly importable
533
+ 2. **Rate Limit Exceeded**: Check outbox rate limits and adjust as needed
534
+ 3. **Failed Messages**: Check error messages in OutboxMessage.error_message
535
+ 4. **Missing Settings**: Verify account settings are properly configured
536
+
537
+ ### Debugging
538
+
539
+ ```python
540
+ # Enable debug logging
541
+ import logging
542
+ logging.getLogger('mojo.apps.notify').setLevel(logging.DEBUG)
543
+
544
+ # Check message status
545
+ message = OutboxMessage.objects.get(id=123)
546
+ print(f"Status: {message.status}")
547
+ print(f"Error: {message.error_message}")
548
+ print(f"Retry count: {message.retry_count}")
549
+ ```
550
+
551
+ ## Performance Tips
552
+
553
+ 1. Use async handlers for high-volume processing
554
+ 2. Configure appropriate batch sizes
555
+ 3. Monitor rate limits and adjust as needed
556
+ 4. Use database indexes on frequently queried fields
557
+ 5. Consider using Celery for background processing
558
+ 6. Implement message archiving for old messages
559
+
560
+ ## Support
561
+
562
+ For issues and questions:
563
+ 1. Check the logs for error messages
564
+ 2. Verify handler configuration
565
+ 3. Test with small batches first
566
+ 4. Monitor rate limits and quotas
File without changes
@@ -0,0 +1,52 @@
1
+ from django.contrib import admin
2
+ from . import models
3
+
4
+
5
+ @admin.register(models.Account)
6
+ class AccountAdmin(admin.ModelAdmin):
7
+ list_display = ['kind', 'domain', 'group', 'is_active', 'created']
8
+ list_filter = ['kind', 'is_active', 'created']
9
+ search_fields = ['domain', 'group__name']
10
+ ordering = ['-created']
11
+
12
+
13
+ @admin.register(models.Inbox)
14
+ class InboxAdmin(admin.ModelAdmin):
15
+ list_display = ['address', 'account', 'is_active', 'created']
16
+ list_filter = ['account__kind', 'is_active', 'created']
17
+ search_fields = ['address', 'account__domain']
18
+ ordering = ['-created']
19
+
20
+
21
+ @admin.register(models.InboxMessage)
22
+ class InboxMessageAdmin(admin.ModelAdmin):
23
+ list_display = ['from_address', 'to_address', 'subject', 'inbox', 'processed', 'created']
24
+ list_filter = ['processed', 'inbox__account__kind', 'created']
25
+ search_fields = ['from_address', 'to_address', 'subject', 'message']
26
+ ordering = ['-created']
27
+ readonly_fields = ['created', 'modified']
28
+
29
+
30
+ @admin.register(models.Outbox)
31
+ class OutboxAdmin(admin.ModelAdmin):
32
+ list_display = ['address', 'account', 'group', 'is_active', 'rate_limit', 'created']
33
+ list_filter = ['account__kind', 'is_active', 'created']
34
+ search_fields = ['address', 'account__domain', 'group__name']
35
+ ordering = ['-created']
36
+
37
+
38
+ @admin.register(models.OutboxMessage)
39
+ class OutboxMessageAdmin(admin.ModelAdmin):
40
+ list_display = ['from_address', 'to_address', 'subject', 'status', 'outbox', 'created']
41
+ list_filter = ['status', 'outbox__account__kind', 'created']
42
+ search_fields = ['from_address', 'to_address', 'subject', 'message']
43
+ ordering = ['-created']
44
+ readonly_fields = ['created', 'modified', 'sent_at', 'failed_at']
45
+
46
+
47
+ # Register existing models
48
+ admin.site.register(models.Message)
49
+ admin.site.register(models.Attachment)
50
+ admin.site.register(models.Bounce)
51
+ admin.site.register(models.Complaint)
52
+ admin.site.register(models.NotifyTemplate)
File without changes