HH/.opencode/plans/mshastra-sms-integration.md
2026-03-28 14:03:56 +03:00

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}"))
```