207 lines
6.6 KiB
Markdown
207 lines
6.6 KiB
Markdown
# Mshastra SMS Integration Plan
|
|
|
|
## Files to modify:
|
|
1. `.env` - Add Mshastra credentials
|
|
2. `.env.example` - Add Mshastra credential placeholders
|
|
3. `config/settings/base.py` - Add Mshastra settings (~line 370)
|
|
4. `apps/notifications/services.py` - Add `_send_sms_mshastra()` and update routing
|
|
5. `apps/notifications/management/commands/test_sms.py` - New management command
|
|
|
|
---
|
|
|
|
## 1. `.env` — Add after last line (after `SMS_API_KEY=simulator-test-key`):
|
|
|
|
```
|
|
# Mshastra SMS API
|
|
MSHASTRA_USERNAME=
|
|
MSHASTRA_PASSWORD=
|
|
MSHASTRA_SENDER_ID=
|
|
```
|
|
|
|
## 2. `.env.example` — Add before the `# Simulator API` comment block (around line 67):
|
|
|
|
```
|
|
# Mshastra SMS API
|
|
# Set SMS_PROVIDER=mshastra and fill in credentials to send real SMS via Mshastra
|
|
MSHASTRA_USERNAME=
|
|
MSHASTRA_PASSWORD=
|
|
MSHASTRA_SENDER_ID=
|
|
```
|
|
|
|
## 3. `config/settings/base.py` — Add after TWILIO settings block (after line 370):
|
|
|
|
```python
|
|
# Mshastra SMS Configuration
|
|
MSHASTRA_USERNAME = env("MSHASTRA_USERNAME", default="")
|
|
MSHASTRA_PASSWORD = env("MSHASTRA_PASSWORD", default="")
|
|
MSHASTRA_SENDER_ID = env("MSHASTRA_SENDER_ID", default="")
|
|
```
|
|
|
|
## 4. `apps/notifications/services.py` — Changes:
|
|
|
|
### 4a. Update `send_sms()` method (lines 55-72)
|
|
Replace the provider routing section to add `mshastra` check:
|
|
|
|
Current code (lines 55-72):
|
|
```python
|
|
sms_config = settings.NOTIFICATION_CHANNELS.get("sms", {})
|
|
provider = sms_config.get("provider", "console")
|
|
|
|
log = NotificationLog.objects.create(
|
|
channel="sms",
|
|
recipient=phone,
|
|
message=message,
|
|
content_object=related_object,
|
|
provider=provider,
|
|
metadata=metadata or {},
|
|
)
|
|
|
|
if provider == "twilio":
|
|
return NotificationService._send_sms_twilio(log, phone, message)
|
|
|
|
logger.info(f"[SMS Console] To: {phone} | Message: {message}")
|
|
log.mark_sent()
|
|
return log
|
|
```
|
|
|
|
New code:
|
|
```python
|
|
sms_config = settings.NOTIFICATION_CHANNELS.get("sms", {})
|
|
provider = sms_config.get("provider", "console")
|
|
|
|
log = NotificationLog.objects.create(
|
|
channel="sms",
|
|
recipient=phone,
|
|
message=message,
|
|
content_object=related_object,
|
|
provider=provider,
|
|
metadata=metadata or {},
|
|
)
|
|
|
|
if provider == "mshastra":
|
|
return NotificationService._send_sms_mshastra(log, phone, message)
|
|
|
|
if provider == "twilio":
|
|
return NotificationService._send_sms_twilio(log, phone, message)
|
|
|
|
logger.info(f"[SMS Console] To: {phone} | Message: {message}")
|
|
log.mark_sent()
|
|
return log
|
|
```
|
|
|
|
### 4b. Add `_send_sms_mshastra()` method after `_send_sms_twilio()` (after line 138):
|
|
|
|
```python
|
|
@staticmethod
|
|
def _send_sms_mshastra(log, phone, message):
|
|
"""
|
|
Send SMS via Mshastra API.
|
|
|
|
Requires: MSHASTRA_USERNAME, MSHASTRA_PASSWORD, and MSHASTRA_SENDER_ID.
|
|
API: https://mshastra.com/sendurl.aspx
|
|
"""
|
|
import requests
|
|
|
|
username = settings.MSHASTRA_USERNAME
|
|
password = settings.MSHASTRA_PASSWORD
|
|
sender_id = settings.MSHASTRA_SENDER_ID
|
|
|
|
if not username or not password:
|
|
logger.warning("Mshastra credentials not configured, falling back to console")
|
|
log.provider = "console"
|
|
log.save(update_fields=["provider"])
|
|
logger.info(f"[SMS Console] To: {phone} | Message: {message}")
|
|
log.mark_sent()
|
|
return log
|
|
|
|
try:
|
|
url = "https://mshastra.com/sendurl.aspx"
|
|
params = {
|
|
"user": username,
|
|
"pwd": password,
|
|
"senderid": sender_id,
|
|
"mobileno": phone,
|
|
"msgtext": message,
|
|
"priority": "High",
|
|
"CountryCode": "ALL",
|
|
}
|
|
|
|
response = requests.get(url, params=params, timeout=30)
|
|
response_text = response.text.strip()
|
|
|
|
log.provider_response = {"status_code": response.status_code, "response": response_text}
|
|
log.save(update_fields=["provider_response"])
|
|
|
|
if "Send Successful" in response_text:
|
|
log.mark_sent()
|
|
logger.info(f"SMS sent via Mshastra to {phone}: {response_text}")
|
|
else:
|
|
log.mark_failed(response_text)
|
|
logger.warning(f"Mshastra SMS failed for {phone}: {response_text}")
|
|
|
|
return log
|
|
|
|
except requests.exceptions.Timeout:
|
|
logger.error(f"Mshastra API timeout for {phone}")
|
|
log.mark_failed("Request timeout")
|
|
return log
|
|
|
|
except requests.exceptions.ConnectionError:
|
|
logger.error(f"Mshastra API connection error for {phone}")
|
|
log.mark_failed("Connection error")
|
|
return log
|
|
|
|
except Exception as e:
|
|
logger.error(f"Unexpected error sending SMS via Mshastra to {phone}: {e}", exc_info=True)
|
|
log.mark_failed(str(e))
|
|
return log
|
|
```
|
|
|
|
## 5. Create `apps/notifications/management/commands/test_sms.py`:
|
|
|
|
```python
|
|
"""
|
|
Management command to test SMS sending.
|
|
|
|
Usage:
|
|
python manage.py test_sms 966501234567
|
|
python manage.py test_sms 966501234567 --message "Custom test message"
|
|
"""
|
|
|
|
from django.core.management.base import BaseCommand
|
|
|
|
from apps.notifications.services import NotificationService
|
|
|
|
|
|
class Command(BaseCommand):
|
|
help = "Send a test SMS to a phone number"
|
|
|
|
def add_arguments(self, parser):
|
|
parser.add_argument("phone", type=str, help="Phone number in international format (e.g. 966501234567)")
|
|
parser.add_argument("--message", type=str, default="Test SMS from PX360", help="Custom message text")
|
|
|
|
def handle(self, *args, **options):
|
|
phone = options["phone"]
|
|
message = options["message"]
|
|
|
|
self.stdout.write(f"Sending SMS to {phone}...")
|
|
self.stdout.write(f"Message: {message}")
|
|
self.stdout.write("")
|
|
|
|
log = NotificationService.send_sms(phone, message)
|
|
|
|
self.stdout.write(f"Status: {log.status}")
|
|
self.stdout.write(f"Provider: {log.provider}")
|
|
if log.error:
|
|
self.stdout.write(self.style.ERROR(f"Error: {log.error}"))
|
|
if log.provider_response:
|
|
self.stdout.write(f"Provider Response: {log.provider_response}")
|
|
|
|
if log.status == "sent":
|
|
self.stdout.write(self.style.SUCCESS(f"SMS sent successfully to {phone}"))
|
|
elif log.status == "failed":
|
|
self.stdout.write(self.style.ERROR(f"SMS failed for {phone}"))
|
|
else:
|
|
self.stdout.write(self.style.WARNING(f"SMS status: {log.status} for {phone}"))
|
|
```
|