django-nativemojo 0.1.10__py3-none-any.whl → 0.1.16__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.
- django_nativemojo-0.1.16.dist-info/METADATA +138 -0
- django_nativemojo-0.1.16.dist-info/RECORD +302 -0
- mojo/__init__.py +1 -1
- mojo/apps/account/management/__init__.py +5 -0
- mojo/apps/account/management/commands/__init__.py +6 -0
- mojo/apps/account/management/commands/serializer_admin.py +651 -0
- mojo/apps/account/migrations/0004_user_avatar.py +20 -0
- mojo/apps/account/migrations/0005_group_last_activity.py +18 -0
- mojo/apps/account/migrations/0006_add_device_tracking_models.py +72 -0
- mojo/apps/account/migrations/0007_delete_userdevicelocation.py +16 -0
- mojo/apps/account/migrations/0008_userdevicelocation.py +33 -0
- mojo/apps/account/migrations/0009_geolocatedip_subnet.py +18 -0
- mojo/apps/account/migrations/0010_group_avatar.py +20 -0
- mojo/apps/account/migrations/0011_user_org_registereddevice_pushconfig_and_more.py +118 -0
- mojo/apps/account/migrations/0012_remove_pushconfig_apns_key_file_and_more.py +21 -0
- mojo/apps/account/migrations/0013_pushconfig_test_mode_alter_pushconfig_apns_enabled_and_more.py +28 -0
- mojo/apps/account/migrations/0014_notificationdelivery_data_payload_and_more.py +48 -0
- mojo/apps/account/models/__init__.py +2 -0
- mojo/apps/account/models/device.py +281 -0
- mojo/apps/account/models/group.py +319 -15
- mojo/apps/account/models/member.py +29 -5
- mojo/apps/account/models/push/__init__.py +4 -0
- mojo/apps/account/models/push/config.py +112 -0
- mojo/apps/account/models/push/delivery.py +93 -0
- mojo/apps/account/models/push/device.py +66 -0
- mojo/apps/account/models/push/template.py +99 -0
- mojo/apps/account/models/user.py +369 -19
- mojo/apps/account/rest/__init__.py +2 -0
- mojo/apps/account/rest/device.py +39 -0
- mojo/apps/account/rest/group.py +9 -0
- mojo/apps/account/rest/push.py +187 -0
- mojo/apps/account/rest/user.py +100 -6
- mojo/apps/account/services/__init__.py +1 -0
- mojo/apps/account/services/push.py +363 -0
- mojo/apps/aws/migrations/0001_initial.py +206 -0
- mojo/apps/aws/migrations/0002_emaildomain_can_recv_emaildomain_can_send_and_more.py +28 -0
- mojo/apps/aws/migrations/0003_mailbox_is_domain_default_mailbox_is_system_default_and_more.py +31 -0
- mojo/apps/aws/migrations/0004_s3bucket.py +39 -0
- mojo/apps/aws/migrations/0005_alter_emaildomain_region_delete_s3bucket.py +21 -0
- mojo/apps/aws/models/__init__.py +19 -0
- mojo/apps/aws/models/email_attachment.py +99 -0
- mojo/apps/aws/models/email_domain.py +218 -0
- mojo/apps/aws/models/email_template.py +132 -0
- mojo/apps/aws/models/incoming_email.py +197 -0
- mojo/apps/aws/models/mailbox.py +288 -0
- mojo/apps/aws/models/sent_message.py +175 -0
- mojo/apps/aws/rest/__init__.py +7 -0
- mojo/apps/aws/rest/email.py +33 -0
- mojo/apps/aws/rest/email_ops.py +183 -0
- mojo/apps/aws/rest/messages.py +32 -0
- mojo/apps/aws/rest/s3.py +64 -0
- mojo/apps/aws/rest/send.py +101 -0
- mojo/apps/aws/rest/sns.py +403 -0
- mojo/apps/aws/rest/templates.py +19 -0
- mojo/apps/aws/services/__init__.py +32 -0
- mojo/apps/aws/services/email.py +390 -0
- mojo/apps/aws/services/email_ops.py +548 -0
- mojo/apps/docit/__init__.py +6 -0
- mojo/apps/docit/markdown_plugins/syntax_highlight.py +25 -0
- mojo/apps/docit/markdown_plugins/toc.py +12 -0
- mojo/apps/docit/migrations/0001_initial.py +113 -0
- mojo/apps/docit/migrations/0002_alter_book_modified_by_alter_page_modified_by.py +26 -0
- mojo/apps/docit/migrations/0003_alter_book_group.py +20 -0
- mojo/apps/docit/models/__init__.py +17 -0
- mojo/apps/docit/models/asset.py +231 -0
- mojo/apps/docit/models/book.py +227 -0
- mojo/apps/docit/models/page.py +319 -0
- mojo/apps/docit/models/page_revision.py +203 -0
- mojo/apps/docit/rest/__init__.py +10 -0
- mojo/apps/docit/rest/asset.py +17 -0
- mojo/apps/docit/rest/book.py +22 -0
- mojo/apps/docit/rest/page.py +22 -0
- mojo/apps/docit/rest/page_revision.py +17 -0
- mojo/apps/docit/services/__init__.py +11 -0
- mojo/apps/docit/services/docit.py +315 -0
- mojo/apps/docit/services/markdown.py +44 -0
- mojo/apps/fileman/README.md +8 -8
- mojo/apps/fileman/backends/base.py +76 -70
- mojo/apps/fileman/backends/filesystem.py +86 -86
- mojo/apps/fileman/backends/s3.py +409 -108
- mojo/apps/fileman/migrations/0001_initial.py +106 -0
- mojo/apps/fileman/migrations/0002_filemanager_parent_alter_filemanager_max_file_size.py +24 -0
- mojo/apps/fileman/migrations/0003_remove_file_fileman_fil_upload__c4bc35_idx_and_more.py +25 -0
- mojo/apps/fileman/migrations/0004_remove_file_original_filename_and_more.py +39 -0
- mojo/apps/fileman/migrations/0005_alter_file_upload_token.py +18 -0
- mojo/apps/fileman/migrations/0006_file_download_url_filemanager_forever_urls.py +23 -0
- mojo/apps/fileman/migrations/0007_remove_filemanager_forever_urls_and_more.py +22 -0
- mojo/apps/fileman/migrations/0008_file_category.py +18 -0
- mojo/apps/fileman/migrations/0009_rename_file_path_file_storage_file_path.py +18 -0
- mojo/apps/fileman/migrations/0010_filerendition.py +33 -0
- mojo/apps/fileman/migrations/0011_alter_filerendition_original_file.py +19 -0
- mojo/apps/fileman/models/__init__.py +1 -5
- mojo/apps/fileman/models/file.py +240 -58
- mojo/apps/fileman/models/manager.py +427 -31
- mojo/apps/fileman/models/rendition.py +118 -0
- mojo/apps/fileman/renderer/__init__.py +111 -0
- mojo/apps/fileman/renderer/audio.py +403 -0
- mojo/apps/fileman/renderer/base.py +205 -0
- mojo/apps/fileman/renderer/document.py +404 -0
- mojo/apps/fileman/renderer/image.py +222 -0
- mojo/apps/fileman/renderer/utils.py +297 -0
- mojo/apps/fileman/renderer/video.py +304 -0
- mojo/apps/fileman/rest/__init__.py +1 -18
- mojo/apps/fileman/rest/upload.py +22 -32
- mojo/apps/fileman/signals.py +58 -0
- mojo/apps/fileman/tasks.py +254 -0
- mojo/apps/fileman/utils/__init__.py +40 -16
- mojo/apps/incident/migrations/0005_incidenthistory.py +39 -0
- mojo/apps/incident/migrations/0006_alter_incident_state.py +18 -0
- mojo/apps/incident/migrations/0007_event_uid.py +18 -0
- mojo/apps/incident/migrations/0008_ticket_ticketnote.py +55 -0
- mojo/apps/incident/migrations/0009_incident_status.py +18 -0
- mojo/apps/incident/migrations/0010_event_country_code.py +18 -0
- mojo/apps/incident/migrations/0011_incident_country_code.py +18 -0
- mojo/apps/incident/migrations/0012_alter_incident_status.py +18 -0
- mojo/apps/incident/models/__init__.py +2 -0
- mojo/apps/incident/models/event.py +35 -0
- mojo/apps/incident/models/history.py +36 -0
- mojo/apps/incident/models/incident.py +3 -1
- mojo/apps/incident/models/ticket.py +62 -0
- mojo/apps/incident/reporter.py +21 -1
- mojo/apps/incident/rest/__init__.py +1 -0
- mojo/apps/incident/rest/event.py +7 -1
- mojo/apps/incident/rest/ticket.py +43 -0
- mojo/apps/jobs/__init__.py +489 -0
- mojo/apps/jobs/adapters.py +24 -0
- mojo/apps/jobs/cli.py +616 -0
- mojo/apps/jobs/daemon.py +370 -0
- mojo/apps/jobs/examples/sample_jobs.py +376 -0
- mojo/apps/jobs/examples/webhook_examples.py +203 -0
- mojo/apps/jobs/handlers/__init__.py +5 -0
- mojo/apps/jobs/handlers/webhook.py +317 -0
- mojo/apps/jobs/job_engine.py +734 -0
- mojo/apps/jobs/keys.py +203 -0
- mojo/apps/jobs/local_queue.py +363 -0
- mojo/apps/jobs/management/__init__.py +3 -0
- mojo/apps/jobs/management/commands/__init__.py +3 -0
- mojo/apps/jobs/manager.py +1327 -0
- mojo/apps/jobs/migrations/0001_initial.py +97 -0
- mojo/apps/jobs/migrations/0002_alter_job_max_retries_joblog.py +39 -0
- mojo/apps/jobs/models/__init__.py +6 -0
- mojo/apps/jobs/models/job.py +441 -0
- mojo/apps/jobs/rest/__init__.py +2 -0
- mojo/apps/jobs/rest/control.py +466 -0
- mojo/apps/jobs/rest/jobs.py +421 -0
- mojo/apps/jobs/scheduler.py +571 -0
- mojo/apps/jobs/services/__init__.py +6 -0
- mojo/apps/jobs/services/job_actions.py +465 -0
- mojo/apps/jobs/settings.py +209 -0
- mojo/apps/logit/migrations/0004_alter_log_level.py +18 -0
- mojo/apps/logit/models/log.py +7 -1
- mojo/apps/metrics/__init__.py +8 -1
- mojo/apps/metrics/redis_metrics.py +198 -0
- mojo/apps/metrics/rest/__init__.py +3 -0
- mojo/apps/metrics/rest/categories.py +266 -0
- mojo/apps/metrics/rest/helpers.py +48 -0
- mojo/apps/metrics/rest/permissions.py +99 -0
- mojo/apps/metrics/rest/values.py +277 -0
- mojo/apps/metrics/utils.py +19 -2
- mojo/decorators/auth.py +6 -1
- mojo/decorators/http.py +47 -3
- mojo/helpers/aws/__init__.py +45 -0
- mojo/helpers/aws/ec2.py +804 -0
- mojo/helpers/aws/iam.py +748 -0
- mojo/helpers/aws/inbound_email.py +309 -0
- mojo/helpers/aws/kms.py +413 -0
- mojo/helpers/aws/s3.py +451 -11
- mojo/helpers/aws/ses.py +483 -0
- mojo/helpers/aws/ses_domain.py +959 -0
- mojo/helpers/aws/sns.py +461 -0
- mojo/helpers/crypto/__init__.py +1 -1
- mojo/helpers/crypto/utils.py +15 -0
- mojo/helpers/dates.py +18 -0
- mojo/helpers/location/__init__.py +2 -0
- mojo/helpers/location/countries.py +262 -0
- mojo/helpers/location/geolocation.py +196 -0
- mojo/helpers/logit.py +37 -0
- mojo/helpers/redis/__init__.py +2 -0
- mojo/helpers/redis/adapter.py +606 -0
- mojo/helpers/redis/client.py +48 -0
- mojo/helpers/redis/pool.py +225 -0
- mojo/helpers/request.py +8 -0
- mojo/helpers/response.py +14 -2
- mojo/helpers/settings/__init__.py +2 -0
- mojo/helpers/{settings.py → settings/helper.py} +1 -37
- mojo/helpers/settings/parser.py +132 -0
- mojo/middleware/auth.py +1 -1
- mojo/middleware/cors.py +40 -0
- mojo/middleware/logging.py +131 -12
- mojo/middleware/mojo.py +10 -0
- mojo/models/rest.py +494 -65
- mojo/models/secrets.py +98 -3
- mojo/serializers/__init__.py +106 -0
- mojo/serializers/core/__init__.py +90 -0
- mojo/serializers/core/cache/__init__.py +121 -0
- mojo/serializers/core/cache/backends.py +518 -0
- mojo/serializers/core/cache/base.py +102 -0
- mojo/serializers/core/cache/disabled.py +181 -0
- mojo/serializers/core/cache/memory.py +287 -0
- mojo/serializers/core/cache/redis.py +533 -0
- mojo/serializers/core/cache/utils.py +454 -0
- mojo/serializers/core/manager.py +550 -0
- mojo/serializers/core/serializer.py +475 -0
- mojo/serializers/examples/settings.py +322 -0
- mojo/serializers/formats/csv.py +393 -0
- mojo/serializers/formats/localizers.py +509 -0
- mojo/serializers/{models.py → simple.py} +38 -15
- mojo/serializers/suggested_improvements.md +388 -0
- testit/client.py +1 -1
- testit/helpers.py +35 -4
- testit/runner.py +23 -6
- django_nativemojo-0.1.10.dist-info/METADATA +0 -96
- django_nativemojo-0.1.10.dist-info/RECORD +0 -194
- mojo/apps/metrics/rest/db.py +0 -0
- mojo/apps/notify/README.md +0 -91
- mojo/apps/notify/README_NOTIFICATIONS.md +0 -566
- mojo/apps/notify/admin.py +0 -52
- mojo/apps/notify/handlers/example_handlers.py +0 -516
- mojo/apps/notify/handlers/ses/__init__.py +0 -25
- mojo/apps/notify/handlers/ses/bounce.py +0 -0
- mojo/apps/notify/handlers/ses/complaint.py +0 -25
- mojo/apps/notify/handlers/ses/message.py +0 -86
- mojo/apps/notify/management/commands/__init__.py +0 -1
- mojo/apps/notify/management/commands/process_notifications.py +0 -370
- mojo/apps/notify/mod +0 -0
- mojo/apps/notify/models/__init__.py +0 -12
- mojo/apps/notify/models/account.py +0 -128
- mojo/apps/notify/models/attachment.py +0 -24
- mojo/apps/notify/models/bounce.py +0 -68
- mojo/apps/notify/models/complaint.py +0 -40
- mojo/apps/notify/models/inbox.py +0 -113
- mojo/apps/notify/models/inbox_message.py +0 -173
- mojo/apps/notify/models/outbox.py +0 -129
- mojo/apps/notify/models/outbox_message.py +0 -288
- mojo/apps/notify/models/template.py +0 -30
- mojo/apps/notify/providers/aws.py +0 -73
- mojo/apps/notify/rest/ses.py +0 -0
- mojo/apps/notify/utils/__init__.py +0 -2
- mojo/apps/notify/utils/notifications.py +0 -404
- mojo/apps/notify/utils/parsing.py +0 -202
- mojo/apps/notify/utils/render.py +0 -144
- mojo/apps/tasks/README.md +0 -118
- mojo/apps/tasks/__init__.py +0 -11
- mojo/apps/tasks/manager.py +0 -489
- mojo/apps/tasks/rest/__init__.py +0 -2
- mojo/apps/tasks/rest/hooks.py +0 -0
- mojo/apps/tasks/rest/tasks.py +0 -62
- mojo/apps/tasks/runner.py +0 -174
- mojo/apps/tasks/tq_handlers.py +0 -14
- mojo/helpers/aws/setup_email.py +0 -0
- mojo/helpers/redis.py +0 -10
- mojo/models/meta.py +0 -262
- mojo/ws4redis/README.md +0 -174
- mojo/ws4redis/__init__.py +0 -2
- mojo/ws4redis/client.py +0 -283
- mojo/ws4redis/connection.py +0 -327
- mojo/ws4redis/exceptions.py +0 -32
- mojo/ws4redis/redis.py +0 -183
- mojo/ws4redis/servers/base.py +0 -86
- mojo/ws4redis/servers/django.py +0 -171
- mojo/ws4redis/servers/uwsgi.py +0 -63
- mojo/ws4redis/settings.py +0 -45
- mojo/ws4redis/utf8validator.py +0 -128
- mojo/ws4redis/websocket.py +0 -403
- {django_nativemojo-0.1.10.dist-info → django_nativemojo-0.1.16.dist-info}/LICENSE +0 -0
- {django_nativemojo-0.1.10.dist-info → django_nativemojo-0.1.16.dist-info}/NOTICE +0 -0
- {django_nativemojo-0.1.10.dist-info → django_nativemojo-0.1.16.dist-info}/WHEEL +0 -0
- /mojo/apps/{notify → aws}/__init__.py +0 -0
- /mojo/apps/{notify/handlers → aws/migrations}/__init__.py +0 -0
- /mojo/apps/{notify/management → docit/markdown_plugins}/__init__.py +0 -0
- /mojo/apps/{notify/providers → docit/migrations}/__init__.py +0 -0
- /mojo/apps/{notify/rest → fileman/migrations}/__init__.py +0 -0
- /mojo/{ws4redis/servers → apps/jobs/examples}/__init__.py +0 -0
- /mojo/apps/{fileman/models/render.py → jobs/migrations/__init__.py} +0 -0
- /mojo/{serializers → rest}/openapi.py +0 -0
- /mojo/{apps/fileman/rest/__init__ → serializers/formats/__init__.py} +0 -0
@@ -1,516 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Example message handlers for the notification system.
|
3
|
-
|
4
|
-
These handlers demonstrate how to process incoming and outgoing messages
|
5
|
-
across different notification channels (email, SMS, WhatsApp, etc.).
|
6
|
-
"""
|
7
|
-
|
8
|
-
import logging
|
9
|
-
import asyncio
|
10
|
-
from typing import Dict, Any
|
11
|
-
from django.conf import settings
|
12
|
-
from django.utils import timezone
|
13
|
-
from django.core.mail import send_mail
|
14
|
-
from django.template import Template, Context
|
15
|
-
|
16
|
-
from ..models import InboxMessage, OutboxMessage, Account
|
17
|
-
from ..utils.notifications import NotificationError
|
18
|
-
|
19
|
-
logger = logging.getLogger(__name__)
|
20
|
-
|
21
|
-
|
22
|
-
# =============================================================================
|
23
|
-
# INBOX MESSAGE HANDLERS (for processing received messages)
|
24
|
-
# =============================================================================
|
25
|
-
|
26
|
-
def on_email_received(inbox_message: InboxMessage) -> bool:
|
27
|
-
"""
|
28
|
-
Example handler for processing received email messages.
|
29
|
-
|
30
|
-
This handler demonstrates:
|
31
|
-
- Basic message processing
|
32
|
-
- Extracting message details
|
33
|
-
- Logging and error handling
|
34
|
-
- Auto-reply functionality
|
35
|
-
|
36
|
-
Usage in Inbox model:
|
37
|
-
sync_handler = "mojo.apps.notify.handlers.example_handlers.on_email_received"
|
38
|
-
"""
|
39
|
-
try:
|
40
|
-
logger.info(f"Processing email from {inbox_message.from_address} to {inbox_message.to_address}")
|
41
|
-
|
42
|
-
# Extract message details
|
43
|
-
sender = inbox_message.from_address
|
44
|
-
recipient = inbox_message.to_address
|
45
|
-
subject = inbox_message.subject or "No Subject"
|
46
|
-
message_body = inbox_message.message
|
47
|
-
|
48
|
-
# Example: Check if this is an auto-reply request
|
49
|
-
if "auto-reply" in subject.lower() or "out of office" in subject.lower():
|
50
|
-
# Send automatic response
|
51
|
-
send_auto_reply(inbox_message)
|
52
|
-
|
53
|
-
# Example: Process support tickets
|
54
|
-
if recipient.startswith("support@"):
|
55
|
-
process_support_ticket(inbox_message)
|
56
|
-
|
57
|
-
# Example: Forward certain messages
|
58
|
-
if should_forward_message(inbox_message):
|
59
|
-
forward_message(inbox_message)
|
60
|
-
|
61
|
-
# Log successful processing
|
62
|
-
logger.info(f"Successfully processed email message {inbox_message.id}")
|
63
|
-
return True
|
64
|
-
|
65
|
-
except Exception as e:
|
66
|
-
logger.error(f"Error processing email message {inbox_message.id}: {e}")
|
67
|
-
return False
|
68
|
-
|
69
|
-
|
70
|
-
async def on_email_received_async(inbox_message: InboxMessage) -> bool:
|
71
|
-
"""
|
72
|
-
Async version of email handler for high-volume processing.
|
73
|
-
|
74
|
-
Usage in Inbox model:
|
75
|
-
async_handler = "mojo.apps.notify.handlers.example_handlers.on_email_received_async"
|
76
|
-
"""
|
77
|
-
try:
|
78
|
-
# Use async processing for heavy operations
|
79
|
-
await asyncio.sleep(0.1) # Simulate async work
|
80
|
-
|
81
|
-
# Process message asynchronously
|
82
|
-
result = await process_email_async(inbox_message)
|
83
|
-
|
84
|
-
logger.info(f"Async processed email message {inbox_message.id}")
|
85
|
-
return result
|
86
|
-
|
87
|
-
except Exception as e:
|
88
|
-
logger.error(f"Async error processing email message {inbox_message.id}: {e}")
|
89
|
-
return False
|
90
|
-
|
91
|
-
|
92
|
-
def on_sms_received(inbox_message: InboxMessage) -> bool:
|
93
|
-
"""
|
94
|
-
Example handler for processing received SMS messages.
|
95
|
-
|
96
|
-
Usage in Inbox model:
|
97
|
-
sync_handler = "mojo.apps.notify.handlers.example_handlers.on_sms_received"
|
98
|
-
"""
|
99
|
-
try:
|
100
|
-
logger.info(f"Processing SMS from {inbox_message.from_address}")
|
101
|
-
|
102
|
-
# Extract SMS content
|
103
|
-
sender_phone = inbox_message.from_address
|
104
|
-
message_text = inbox_message.message.strip()
|
105
|
-
|
106
|
-
# Example: Handle SMS commands
|
107
|
-
if message_text.upper().startswith("STOP"):
|
108
|
-
handle_sms_unsubscribe(sender_phone)
|
109
|
-
elif message_text.upper().startswith("START"):
|
110
|
-
handle_sms_subscribe(sender_phone)
|
111
|
-
elif message_text.upper().startswith("HELP"):
|
112
|
-
send_sms_help(sender_phone)
|
113
|
-
else:
|
114
|
-
# Process regular SMS message
|
115
|
-
process_sms_message(inbox_message)
|
116
|
-
|
117
|
-
return True
|
118
|
-
|
119
|
-
except Exception as e:
|
120
|
-
logger.error(f"Error processing SMS message {inbox_message.id}: {e}")
|
121
|
-
return False
|
122
|
-
|
123
|
-
|
124
|
-
def on_whatsapp_received(inbox_message: InboxMessage) -> bool:
|
125
|
-
"""
|
126
|
-
Example handler for processing received WhatsApp messages.
|
127
|
-
|
128
|
-
Usage in Inbox model:
|
129
|
-
sync_handler = "mojo.apps.notify.handlers.example_handlers.on_whatsapp_received"
|
130
|
-
"""
|
131
|
-
try:
|
132
|
-
logger.info(f"Processing WhatsApp from {inbox_message.from_address}")
|
133
|
-
|
134
|
-
# Extract message metadata (WhatsApp specific)
|
135
|
-
metadata = inbox_message.metadata
|
136
|
-
message_type = metadata.get('message_type', 'text')
|
137
|
-
|
138
|
-
if message_type == 'text':
|
139
|
-
process_whatsapp_text(inbox_message)
|
140
|
-
elif message_type == 'image':
|
141
|
-
process_whatsapp_image(inbox_message)
|
142
|
-
elif message_type == 'document':
|
143
|
-
process_whatsapp_document(inbox_message)
|
144
|
-
|
145
|
-
return True
|
146
|
-
|
147
|
-
except Exception as e:
|
148
|
-
logger.error(f"Error processing WhatsApp message {inbox_message.id}: {e}")
|
149
|
-
return False
|
150
|
-
|
151
|
-
|
152
|
-
# =============================================================================
|
153
|
-
# OUTBOX MESSAGE HANDLERS (for sending messages)
|
154
|
-
# =============================================================================
|
155
|
-
|
156
|
-
def on_email_send(outbox_message: OutboxMessage) -> bool:
|
157
|
-
"""
|
158
|
-
Example handler for sending email messages.
|
159
|
-
|
160
|
-
Usage in Outbox model:
|
161
|
-
handler = "mojo.apps.notify.handlers.example_handlers.on_email_send"
|
162
|
-
"""
|
163
|
-
try:
|
164
|
-
# Mark as sending
|
165
|
-
outbox_message.mark_sending()
|
166
|
-
|
167
|
-
# Extract email details
|
168
|
-
recipient = outbox_message.to_address
|
169
|
-
sender = outbox_message.from_address
|
170
|
-
subject = outbox_message.subject or "No Subject"
|
171
|
-
message_body = outbox_message.message
|
172
|
-
|
173
|
-
# Get email settings from account
|
174
|
-
account = outbox_message.account
|
175
|
-
smtp_settings = account.get_setting('smtp', {})
|
176
|
-
|
177
|
-
# Send email using Django's email backend or custom SMTP
|
178
|
-
if smtp_settings:
|
179
|
-
send_via_custom_smtp(outbox_message, smtp_settings)
|
180
|
-
else:
|
181
|
-
send_via_django_email(outbox_message)
|
182
|
-
|
183
|
-
# Mark as sent
|
184
|
-
outbox_message.mark_sent()
|
185
|
-
logger.info(f"Email sent successfully to {recipient}")
|
186
|
-
return True
|
187
|
-
|
188
|
-
except Exception as e:
|
189
|
-
error_msg = f"Failed to send email to {outbox_message.to_address}: {e}"
|
190
|
-
outbox_message.mark_failed(error_msg)
|
191
|
-
logger.error(error_msg)
|
192
|
-
return False
|
193
|
-
|
194
|
-
|
195
|
-
def on_sms_send(outbox_message: OutboxMessage) -> bool:
|
196
|
-
"""
|
197
|
-
Example handler for sending SMS messages.
|
198
|
-
|
199
|
-
Usage in Outbox model:
|
200
|
-
handler = "mojo.apps.notify.handlers.example_handlers.on_sms_send"
|
201
|
-
"""
|
202
|
-
try:
|
203
|
-
outbox_message.mark_sending()
|
204
|
-
|
205
|
-
# Extract SMS details
|
206
|
-
phone_number = outbox_message.to_address
|
207
|
-
message_text = outbox_message.message
|
208
|
-
|
209
|
-
# Get SMS provider settings
|
210
|
-
account = outbox_message.account
|
211
|
-
sms_settings = account.get_setting('sms_provider', {})
|
212
|
-
|
213
|
-
provider = sms_settings.get('provider', 'twilio')
|
214
|
-
|
215
|
-
if provider == 'twilio':
|
216
|
-
send_via_twilio(outbox_message, sms_settings)
|
217
|
-
elif provider == 'aws_sns':
|
218
|
-
send_via_aws_sns(outbox_message, sms_settings)
|
219
|
-
else:
|
220
|
-
raise NotificationError(f"Unknown SMS provider: {provider}")
|
221
|
-
|
222
|
-
outbox_message.mark_sent()
|
223
|
-
logger.info(f"SMS sent successfully to {phone_number}")
|
224
|
-
return True
|
225
|
-
|
226
|
-
except Exception as e:
|
227
|
-
error_msg = f"Failed to send SMS to {outbox_message.to_address}: {e}"
|
228
|
-
outbox_message.mark_failed(error_msg)
|
229
|
-
logger.error(error_msg)
|
230
|
-
return False
|
231
|
-
|
232
|
-
|
233
|
-
def on_whatsapp_send(outbox_message: OutboxMessage) -> bool:
|
234
|
-
"""
|
235
|
-
Example handler for sending WhatsApp messages.
|
236
|
-
|
237
|
-
Usage in Outbox model:
|
238
|
-
handler = "mojo.apps.notify.handlers.example_handlers.on_whatsapp_send"
|
239
|
-
"""
|
240
|
-
try:
|
241
|
-
outbox_message.mark_sending()
|
242
|
-
|
243
|
-
# Extract WhatsApp details
|
244
|
-
phone_number = outbox_message.to_address
|
245
|
-
message_text = outbox_message.message
|
246
|
-
|
247
|
-
# Get WhatsApp Business API settings
|
248
|
-
account = outbox_message.account
|
249
|
-
wa_settings = account.get_setting('whatsapp_api', {})
|
250
|
-
|
251
|
-
# Send via WhatsApp Business API
|
252
|
-
send_via_whatsapp_api(outbox_message, wa_settings)
|
253
|
-
|
254
|
-
outbox_message.mark_sent()
|
255
|
-
logger.info(f"WhatsApp sent successfully to {phone_number}")
|
256
|
-
return True
|
257
|
-
|
258
|
-
except Exception as e:
|
259
|
-
error_msg = f"Failed to send WhatsApp to {outbox_message.to_address}: {e}"
|
260
|
-
outbox_message.mark_failed(error_msg)
|
261
|
-
logger.error(error_msg)
|
262
|
-
return False
|
263
|
-
|
264
|
-
|
265
|
-
def on_push_send(outbox_message: OutboxMessage) -> bool:
|
266
|
-
"""
|
267
|
-
Example handler for sending push notifications.
|
268
|
-
|
269
|
-
Usage in Outbox model:
|
270
|
-
handler = "mojo.apps.notify.handlers.example_handlers.on_push_send"
|
271
|
-
"""
|
272
|
-
try:
|
273
|
-
outbox_message.mark_sending()
|
274
|
-
|
275
|
-
# Extract push notification details
|
276
|
-
device_token = outbox_message.to_address
|
277
|
-
title = outbox_message.subject or "Notification"
|
278
|
-
message_body = outbox_message.message
|
279
|
-
metadata = outbox_message.metadata
|
280
|
-
|
281
|
-
# Get push notification settings
|
282
|
-
account = outbox_message.account
|
283
|
-
push_settings = account.get_setting('push_provider', {})
|
284
|
-
|
285
|
-
provider = push_settings.get('provider', 'fcm')
|
286
|
-
|
287
|
-
if provider == 'fcm':
|
288
|
-
send_via_fcm(outbox_message, push_settings)
|
289
|
-
elif provider == 'apns':
|
290
|
-
send_via_apns(outbox_message, push_settings)
|
291
|
-
else:
|
292
|
-
raise NotificationError(f"Unknown push provider: {provider}")
|
293
|
-
|
294
|
-
outbox_message.mark_sent()
|
295
|
-
logger.info(f"Push notification sent successfully to {device_token}")
|
296
|
-
return True
|
297
|
-
|
298
|
-
except Exception as e:
|
299
|
-
error_msg = f"Failed to send push notification to {outbox_message.to_address}: {e}"
|
300
|
-
outbox_message.mark_failed(error_msg)
|
301
|
-
logger.error(error_msg)
|
302
|
-
return False
|
303
|
-
|
304
|
-
|
305
|
-
# =============================================================================
|
306
|
-
# HELPER FUNCTIONS
|
307
|
-
# =============================================================================
|
308
|
-
|
309
|
-
def send_auto_reply(inbox_message: InboxMessage):
|
310
|
-
"""Send an automatic reply to the sender"""
|
311
|
-
from ..utils.notifications import send_email
|
312
|
-
|
313
|
-
auto_reply_template = """
|
314
|
-
Thank you for your message. This is an automated response.
|
315
|
-
|
316
|
-
We have received your email and will respond within 24 hours.
|
317
|
-
|
318
|
-
Original subject: {subject}
|
319
|
-
Received at: {received_at}
|
320
|
-
"""
|
321
|
-
|
322
|
-
reply_message = auto_reply_template.format(
|
323
|
-
subject=inbox_message.subject or "No Subject",
|
324
|
-
received_at=inbox_message.created.strftime("%Y-%m-%d %H:%M:%S")
|
325
|
-
)
|
326
|
-
|
327
|
-
send_email(
|
328
|
-
to_address=inbox_message.from_address,
|
329
|
-
subject=f"Re: {inbox_message.subject or 'Your Message'}",
|
330
|
-
message=reply_message,
|
331
|
-
from_address=inbox_message.to_address
|
332
|
-
)
|
333
|
-
|
334
|
-
|
335
|
-
def process_support_ticket(inbox_message: InboxMessage):
|
336
|
-
"""Process a support ticket from email"""
|
337
|
-
# This would integrate with your ticketing system
|
338
|
-
logger.info(f"Creating support ticket for: {inbox_message.subject}")
|
339
|
-
|
340
|
-
# Example: Create a ticket record
|
341
|
-
ticket_data = {
|
342
|
-
'email': inbox_message.from_address,
|
343
|
-
'subject': inbox_message.subject,
|
344
|
-
'description': inbox_message.message,
|
345
|
-
'created_at': inbox_message.created,
|
346
|
-
}
|
347
|
-
|
348
|
-
# Save to your ticketing system
|
349
|
-
# Ticket.objects.create(**ticket_data)
|
350
|
-
|
351
|
-
|
352
|
-
def should_forward_message(inbox_message: InboxMessage) -> bool:
|
353
|
-
"""Determine if message should be forwarded"""
|
354
|
-
# Example logic for forwarding
|
355
|
-
keywords = ['urgent', 'asap', 'emergency']
|
356
|
-
message_lower = inbox_message.message.lower()
|
357
|
-
subject_lower = (inbox_message.subject or "").lower()
|
358
|
-
|
359
|
-
return any(keyword in message_lower or keyword in subject_lower for keyword in keywords)
|
360
|
-
|
361
|
-
|
362
|
-
def forward_message(inbox_message: InboxMessage):
|
363
|
-
"""Forward message to designated recipients"""
|
364
|
-
from ..utils.notifications import send_email
|
365
|
-
|
366
|
-
forward_addresses = ['manager@example.com', 'support-lead@example.com']
|
367
|
-
|
368
|
-
forward_message = f"""
|
369
|
-
Forwarded message from: {inbox_message.from_address}
|
370
|
-
Original recipient: {inbox_message.to_address}
|
371
|
-
Subject: {inbox_message.subject}
|
372
|
-
Received: {inbox_message.created}
|
373
|
-
|
374
|
-
Message:
|
375
|
-
{inbox_message.message}
|
376
|
-
"""
|
377
|
-
|
378
|
-
for address in forward_addresses:
|
379
|
-
send_email(
|
380
|
-
to_address=address,
|
381
|
-
subject=f"FWD: {inbox_message.subject}",
|
382
|
-
message=forward_message,
|
383
|
-
from_address=inbox_message.to_address
|
384
|
-
)
|
385
|
-
|
386
|
-
|
387
|
-
async def process_email_async(inbox_message: InboxMessage) -> bool:
|
388
|
-
"""Async email processing"""
|
389
|
-
# Simulate async work like API calls, database operations, etc.
|
390
|
-
await asyncio.sleep(0.1)
|
391
|
-
|
392
|
-
# Process message
|
393
|
-
return True
|
394
|
-
|
395
|
-
|
396
|
-
def handle_sms_unsubscribe(phone_number: str):
|
397
|
-
"""Handle SMS unsubscribe request"""
|
398
|
-
logger.info(f"Processing SMS unsubscribe for {phone_number}")
|
399
|
-
# Add to unsubscribe list
|
400
|
-
# SMSUnsubscribe.objects.get_or_create(phone_number=phone_number)
|
401
|
-
|
402
|
-
|
403
|
-
def handle_sms_subscribe(phone_number: str):
|
404
|
-
"""Handle SMS subscribe request"""
|
405
|
-
logger.info(f"Processing SMS subscribe for {phone_number}")
|
406
|
-
# Remove from unsubscribe list
|
407
|
-
# SMSUnsubscribe.objects.filter(phone_number=phone_number).delete()
|
408
|
-
|
409
|
-
|
410
|
-
def send_sms_help(phone_number: str):
|
411
|
-
"""Send SMS help message"""
|
412
|
-
from ..utils.notifications import send_sms
|
413
|
-
|
414
|
-
help_message = """
|
415
|
-
SMS Commands:
|
416
|
-
- STOP: Unsubscribe from messages
|
417
|
-
- START: Subscribe to messages
|
418
|
-
- HELP: Show this help message
|
419
|
-
"""
|
420
|
-
|
421
|
-
send_sms(to_address=phone_number, message=help_message)
|
422
|
-
|
423
|
-
|
424
|
-
def process_sms_message(inbox_message: InboxMessage):
|
425
|
-
"""Process regular SMS message"""
|
426
|
-
logger.info(f"Processing SMS message: {inbox_message.message[:50]}...")
|
427
|
-
|
428
|
-
|
429
|
-
def process_whatsapp_text(inbox_message: InboxMessage):
|
430
|
-
"""Process WhatsApp text message"""
|
431
|
-
logger.info(f"Processing WhatsApp text: {inbox_message.message[:50]}...")
|
432
|
-
|
433
|
-
|
434
|
-
def process_whatsapp_image(inbox_message: InboxMessage):
|
435
|
-
"""Process WhatsApp image message"""
|
436
|
-
logger.info("Processing WhatsApp image message")
|
437
|
-
# Handle image processing
|
438
|
-
|
439
|
-
|
440
|
-
def process_whatsapp_document(inbox_message: InboxMessage):
|
441
|
-
"""Process WhatsApp document message"""
|
442
|
-
logger.info("Processing WhatsApp document message")
|
443
|
-
# Handle document processing
|
444
|
-
|
445
|
-
|
446
|
-
def send_via_custom_smtp(outbox_message: OutboxMessage, smtp_settings: Dict[str, Any]):
|
447
|
-
"""Send email via custom SMTP settings"""
|
448
|
-
import smtplib
|
449
|
-
from email.mime.text import MIMEText
|
450
|
-
from email.mime.multipart import MIMEMultipart
|
451
|
-
|
452
|
-
# Create message
|
453
|
-
msg = MIMEMultipart()
|
454
|
-
msg['From'] = outbox_message.from_address
|
455
|
-
msg['To'] = outbox_message.to_address
|
456
|
-
msg['Subject'] = outbox_message.subject or "No Subject"
|
457
|
-
|
458
|
-
# Add body
|
459
|
-
msg.attach(MIMEText(outbox_message.message, 'plain'))
|
460
|
-
|
461
|
-
# Connect and send
|
462
|
-
server = smtplib.SMTP(smtp_settings['host'], smtp_settings.get('port', 587))
|
463
|
-
if smtp_settings.get('use_tls', True):
|
464
|
-
server.starttls()
|
465
|
-
|
466
|
-
if smtp_settings.get('username') and smtp_settings.get('password'):
|
467
|
-
server.login(smtp_settings['username'], smtp_settings['password'])
|
468
|
-
|
469
|
-
server.send_message(msg)
|
470
|
-
server.quit()
|
471
|
-
|
472
|
-
|
473
|
-
def send_via_django_email(outbox_message: OutboxMessage):
|
474
|
-
"""Send email via Django's default email backend"""
|
475
|
-
send_mail(
|
476
|
-
subject=outbox_message.subject or "No Subject",
|
477
|
-
message=outbox_message.message,
|
478
|
-
from_email=outbox_message.from_address,
|
479
|
-
recipient_list=[outbox_message.to_address],
|
480
|
-
fail_silently=False
|
481
|
-
)
|
482
|
-
|
483
|
-
|
484
|
-
def send_via_twilio(outbox_message: OutboxMessage, sms_settings: Dict[str, Any]):
|
485
|
-
"""Send SMS via Twilio"""
|
486
|
-
# This would use the Twilio Python SDK
|
487
|
-
logger.info(f"Sending SMS via Twilio to {outbox_message.to_address}")
|
488
|
-
# from twilio.rest import Client
|
489
|
-
# client = Client(sms_settings['account_sid'], sms_settings['auth_token'])
|
490
|
-
# message = client.messages.create(...)
|
491
|
-
|
492
|
-
|
493
|
-
def send_via_aws_sns(outbox_message: OutboxMessage, sms_settings: Dict[str, Any]):
|
494
|
-
"""Send SMS via AWS SNS"""
|
495
|
-
logger.info(f"Sending SMS via AWS SNS to {outbox_message.to_address}")
|
496
|
-
# import boto3
|
497
|
-
# sns = boto3.client('sns', ...)
|
498
|
-
# sns.publish(...)
|
499
|
-
|
500
|
-
|
501
|
-
def send_via_whatsapp_api(outbox_message: OutboxMessage, wa_settings: Dict[str, Any]):
|
502
|
-
"""Send WhatsApp message via Business API"""
|
503
|
-
logger.info(f"Sending WhatsApp via API to {outbox_message.to_address}")
|
504
|
-
# Implement WhatsApp Business API call
|
505
|
-
|
506
|
-
|
507
|
-
def send_via_fcm(outbox_message: OutboxMessage, push_settings: Dict[str, Any]):
|
508
|
-
"""Send push notification via Firebase Cloud Messaging"""
|
509
|
-
logger.info(f"Sending FCM push to {outbox_message.to_address}")
|
510
|
-
# Implement FCM API call
|
511
|
-
|
512
|
-
|
513
|
-
def send_via_apns(outbox_message: OutboxMessage, push_settings: Dict[str, Any]):
|
514
|
-
"""Send push notification via Apple Push Notification Service"""
|
515
|
-
logger.info(f"Sending APNS push to {outbox_message.to_address}")
|
516
|
-
# Implement APNS API call
|
@@ -1,25 +0,0 @@
|
|
1
|
-
from .message import on_message, on_confirm_subscription, on_notification, JsonResponse
|
2
|
-
from .complaint import on_complaint
|
3
|
-
from .bounce import on_bounce
|
4
|
-
from mojo.helpers import modules
|
5
|
-
|
6
|
-
INBOX_HANDLERS = {
|
7
|
-
"Received": on_message,
|
8
|
-
"SubscriptionConfirmation": on_confrim_subscription,
|
9
|
-
"Complaint": on_complaint,
|
10
|
-
"Notification": on_notification,
|
11
|
-
"Bounce": on_bounce
|
12
|
-
|
13
|
-
}
|
14
|
-
|
15
|
-
def handle_request(request):
|
16
|
-
msg = request.DATA.get("Message", "")
|
17
|
-
handler = INBOX_HANDLERS.get(msg.notificationType, None)
|
18
|
-
if handler is not None:
|
19
|
-
handler(request, msg)
|
20
|
-
else:
|
21
|
-
Event = modules.get_model("incidents", "Event")
|
22
|
-
if Event is None:
|
23
|
-
Event.report(f"no ses mailman handler for: {msg.notificationType}",
|
24
|
-
"mailman_ses", raw_message=request.DATA)
|
25
|
-
return JsonResponse(dict(status=True))
|
File without changes
|
@@ -1,25 +0,0 @@
|
|
1
|
-
from mojo.apps.mailmain.models import Complaint
|
2
|
-
from mojo.models import User
|
3
|
-
|
4
|
-
|
5
|
-
def record(kind, address, reason, user_agent=None, code=None, source=None, source_ip=None, user=None):
|
6
|
-
complaint = Complaint(
|
7
|
-
kind=kind,
|
8
|
-
address=address,
|
9
|
-
reason=reason,
|
10
|
-
user_agent=user_agent,
|
11
|
-
code=code,
|
12
|
-
source=source,
|
13
|
-
source_ip=source_ip
|
14
|
-
)
|
15
|
-
|
16
|
-
if user is None:
|
17
|
-
user = User.objects.filter(email=address).last()
|
18
|
-
if user:
|
19
|
-
user.is_email_verified = False
|
20
|
-
user.save()
|
21
|
-
user.log(f"email complaint: {kind} to {address} from {source_ip}", kind="complaint", level="warning")
|
22
|
-
user.log("Email notifications have been disabled because of complaints.", kind="email", level="warning")
|
23
|
-
|
24
|
-
complaint.user = user
|
25
|
-
complaint.save()
|
@@ -1,86 +0,0 @@
|
|
1
|
-
from mojo.helpers.response import JsonResponse
|
2
|
-
from mojo.apps.mailman import utils
|
3
|
-
from mojo.apps.mailman.models import Message
|
4
|
-
import requests
|
5
|
-
|
6
|
-
|
7
|
-
def on_confrim_subscription(request):
|
8
|
-
url = request.DATA.get("SubscribeURL", None)
|
9
|
-
resp = requests.get(url)
|
10
|
-
# TODO record this as an event/incident
|
11
|
-
return JsonResponse(dict(status=True))
|
12
|
-
|
13
|
-
|
14
|
-
def on_message(request):
|
15
|
-
"""
|
16
|
-
Email receiving can be configured in 2 ways.
|
17
|
-
1. raw email via SNS which would have content field
|
18
|
-
2. s3 bucket stored email which will have a receipt.action.bucketName
|
19
|
-
"""
|
20
|
-
msg = request.DATA
|
21
|
-
if msg.content is None and msg.receipt and msg.receipt.action:
|
22
|
-
action = msg.receipt.action
|
23
|
-
if action.type == "S3":
|
24
|
-
return on_s3_email(request, msg, action.bucketName, action.objectKey)
|
25
|
-
|
26
|
-
if msg.content is None:
|
27
|
-
logger.error("message has no content", msg)
|
28
|
-
return
|
29
|
-
|
30
|
-
on_raw_email(utils.parse_raw_message(msg.content))
|
31
|
-
return
|
32
|
-
|
33
|
-
|
34
|
-
def on_s3_email(request, msg, bucket_name, object_key):
|
35
|
-
msg_data = utils.parse_raw_message(s3store.getObjectContent(bucket_name, object_key))
|
36
|
-
return on_raw_email(request, msg, msg_data)
|
37
|
-
|
38
|
-
|
39
|
-
def on_raw_email(request, imsg, msg_data):
|
40
|
-
to_email = imsg.receipt.recipients[0]
|
41
|
-
# logger.info("parsed", msg_data)
|
42
|
-
msg = createMessage(to_email, msg_data)
|
43
|
-
metrics.metric("emails_received", category="email", min_granularity="hourly")
|
44
|
-
|
45
|
-
attachments = []
|
46
|
-
for msg_atch in msg_data.attachments:
|
47
|
-
atch = Attachment(message=msg, name=msg_atch.name, content_type=msg_atch.content_type)
|
48
|
-
if msg_atch.encoding == "base64":
|
49
|
-
atch.saveMediaFile(msg_atch.payload, "media", msg_atch.name, is_base64=True)
|
50
|
-
elif msg_atch.encoding == "quoted-printable":
|
51
|
-
obj = mailtils.toFileObject(msg_atch)
|
52
|
-
atch.saveMediaFile(obj, "media", msg_atch.name)
|
53
|
-
else:
|
54
|
-
logger.error("unknown encoding", msg_atch.encoding)
|
55
|
-
continue
|
56
|
-
atch.save()
|
57
|
-
attachments.append(atch)
|
58
|
-
# add the recipients mailbox
|
59
|
-
addToMailbox(msg)
|
60
|
-
# now lets check if we have more recipients
|
61
|
-
for to_email in imsg.receipt.recipients[1:]:
|
62
|
-
msg = createMessage(to_email, msg_data)
|
63
|
-
for atch in attachments:
|
64
|
-
# create a copy of the attachment, for the new msg
|
65
|
-
atch.pk = None
|
66
|
-
atch.message = msg
|
67
|
-
atch.save()
|
68
|
-
addToMailbox(msg)
|
69
|
-
return rv.restStatus(request, True)
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
def create_message(to_email, msg_data):
|
74
|
-
msg = Message(
|
75
|
-
to_email=to_email,
|
76
|
-
sent_at=msg_data.sent_at,
|
77
|
-
subject=msg_data.subject,
|
78
|
-
message=msg_data.message,
|
79
|
-
html=msg_data.html,
|
80
|
-
body=msg_data.body,
|
81
|
-
to=msg_data.to,
|
82
|
-
cc=msg_data.cc,
|
83
|
-
from_email=msg_data.from_email,
|
84
|
-
from_name=msg_data.from_name)
|
85
|
-
msg.save()
|
86
|
-
return msg
|
@@ -1 +0,0 @@
|
|
1
|
-
```
|