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