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

6.6 KiB

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):

# 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):

        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:

        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):

    @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:

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