Send SMS from Django application using any SMS service provider just writing a single line of code.
from django.contrib import admin
from django.http.request import HttpRequest

from send_sms.models import SMS_Log, SMSParameters, SMSSettings

# Register your models here.


class SMSParametersAdmin(admin.TabularInline):
    model = SMSParameters


class SMSSettingsAdmin(admin.ModelAdmin):
    inlines = [SMSParametersAdmin]

    def has_add_permission(self, request: HttpRequest) -> bool:
        return not SMSSettings.objects.exists()


@admin.register(SMS_Log)
class SMSLogAdmin(admin.ModelAdmin):
    list_display = ['sent_to', 'sent_on', 'message']
    readonly_fields = ['sent_to', 'sent_on', 'message']


admin.site.register(SMSSettings, SMSSettingsAdmin)

build/lib/send_sms/admin.py
hizbul25/django-send-sms
from django.apps import AppConfig


class SendSmsConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'send_sms'

build/lib/send_sms/apps.py
hizbul25/django-send-sms
# Generated by Django 4.2.5 on 2023-09-07 03:31

from django.db import migrations, models


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='SMS_Log',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('sent_on', models.DateTimeField(auto_now=True)),
                ('message', models.TextField()),
                ('sent_to', models.CharField(max_length=28)),
            ],
        ),
        migrations.CreateModel(
            name='SMSSettings',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('SMS Gateway URL', models.URLField(help_text='Eg. smsgateway.com/api/send_sms.cgi')),
                ('message_parameter', models.CharField(help_text='Enter url parameter for message', max_length=64)),
                ('receiver_parameter', models.CharField(help_text='Enter url parameter for receiver nos', max_length=64)),
                ('Static Parameters', models.JSONField(help_text='Enter static url parameters here (Eg. sender=django, username=django, password=1234 etc.)')),
                ('Use POST', models.BooleanField(default=False)),
            ],
        ),
    ]

build/lib/send_sms/migrations/0001_initial.py
hizbul25/django-send-sms
# Generated by Django 4.2.5 on 2023-09-08 08:20

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    dependencies = [
        ('send_sms', '0001_initial'),
    ]

    operations = [
        migrations.RemoveField(
            model_name='smssettings',
            name='Static Parameters',
        ),
        migrations.CreateModel(
            name='SMSParameters',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('Parameter', models.CharField(max_length=128)),
                ('value', models.CharField(max_length=128)),
                ('header', models.BooleanField(default=False)),
                ('sms_settings', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='send_sms.smssettings')),
            ],
        ),
    ]

build/lib/send_sms/migrations/0002_remove_smssettings_static_parameters_smsparameters.py
hizbul25/django-send-sms
# Generated by Django 4.2.5 on 2023-09-08 09:44

from django.db import migrations


class Migration(migrations.Migration):

    dependencies = [
        ('send_sms', '0002_remove_smssettings_static_parameters_smsparameters'),
    ]

    operations = [
        migrations.RenameField(
            model_name='smsparameters',
            old_name='Parameter',
            new_name='parameter',
        ),
        migrations.RenameField(
            model_name='smssettings',
            old_name='SMS Gateway URL',
            new_name='sms_gateway_url',
        ),
        migrations.RenameField(
            model_name='smssettings',
            old_name='Use POST',
            new_name='use_post',
        ),
    ]

build/lib/send_sms/migrations/0003_rename_parameter_smsparameters_parameter_and_more.py
hizbul25/django-send-sms
from django.db import models

# Create your models here.


class SMSSettings(models.Model):
    sms_gateway_url = models.URLField(
        blank=False, help_text='Eg. smsgateway.com/api/send_sms.cgi')
    message_parameter = models.CharField(
        blank=False, help_text='Enter url parameter for message', max_length=64)
    receiver_parameter = models.CharField(
        blank=False, help_text='Enter url parameter for receiver nos', max_length=64)
    use_post = models.BooleanField(default=False)

    class Meta:
        verbose_name_plural = 'SMS Settings'

    def __str__(self) -> str:
        return 'SMS Settings'


class SMSParameters(models.Model):
    parameter = models.CharField(max_length=128, blank=False)
    value = models.CharField(max_length=128, blank=False)
    header = models.BooleanField(default=False)
    sms_settings = models.ForeignKey(SMSSettings, on_delete=models.CASCADE)
    
    def __str__(self) -> str:
        return self.parameter

class SMS_Log(models.Model):
    sent_on = models.DateTimeField(auto_now=True)
    message = models.TextField()
    sent_to = models.CharField(max_length=28)

    class Meta:
        verbose_name_plural = 'SMS Logs'

build/lib/send_sms/models.py
hizbul25/django-send-sms
from django.core.exceptions import ValidationError
from django.test import TestCase

from send_sms.views import validate_receiver_nos

# Create your tests here.


class SMSTestCase(TestCase):
    def test_validate_receiver_nos(self):
        response = validate_receiver_nos(['01671589788', '01671589799'])
        self.assertEqual(len(response), 2)
        with self.assertRaises(ValidationError, msg='Please enter valid mobile nos'):
            validate_receiver_nos([''])

build/lib/send_sms/tests.py
hizbul25/django-send-sms
from django.core.exceptions import ObjectDoesNotExist, ValidationError

from send_sms.models import SMS_Log, SMSParameters, SMSSettings

# Create your views here.


def validate_receiver_nos(receiver_list):
    validated_receiver_list = []
    for d in receiver_list:
        if not d:
            break

        # remove invalid character
        for x in [" ", "-", "(", ")"]:
            d = d.replace(x, "")

        validated_receiver_list.append(d)

    if not validated_receiver_list:
        raise ValidationError("Please enter valid mobile nos")

    return validated_receiver_list


def send_sms(receiver_list: list, msg: str, sender_name="", success_msg=True):

    import json

    if isinstance(receiver_list, str):
        receiver_list = json.loads(receiver_list)
        if not isinstance(receiver_list, list):
            receiver_list = [receiver_list]

    receiver_list = validate_receiver_nos(receiver_list)

    arg = {
        "receiver_list": receiver_list,
        "message": msg.encode("utf-8"),
        "success_msg": success_msg,
    }
    if SMSSettings.objects.first():
        send_via_gateway(arg)
    else:
        raise ObjectDoesNotExist("Please Update SMS Settings")


def send_via_gateway(arg):
    ss = SMSSettings.objects.first()
    headers = get_headers()
    use_json = headers.get("Content-Type") == "application/json"

    message = arg.get("message")
    args = {ss.message_parameter: message}
    parameters = SMSParameters.objects.filter(sms_settings=ss)
    for d in parameters:
        if not d.header:
            args[d.parameter] = d.value

    success_list = []
    for d in arg.get("receiver_list"):
        args[ss.receiver_parameter] = d
        status = send_request(ss.sms_gateway_url, args,
                              headers, ss.use_post, use_json)

        if 200 <= status < 300:
            success_list.append(d)

    if len(success_list) > 0:
        args.update(arg)
        create_sms_log(args, success_list)
        if arg.get("success_msg"):
            print(f"SMS sent to following numbers: {', '.join(success_list)}")


def get_headers(sms_settings=None):
    if not sms_settings:
        sms_settings = SMSSettings.objects.first()
        parameters = SMSParameters.objects.filter(sms_settings=sms_settings)

    headers = {"Accept": "text/plain, text/html, */*"}
    for d in parameters:
        if d.header == True:
            headers.update({d.parameter: d.value})

    return headers


def send_request(gateway_url, params, headers=None, use_post=False, use_json=False):
    import requests

    if not headers:
        headers = get_headers()
    kwargs = {"headers": headers}

    if use_json:
        kwargs["json"] = params
    elif use_post:
        kwargs["data"] = params
    else:
        kwargs["params"] = params

    if use_post:
        response = requests.post(gateway_url, **kwargs)
    else:
        response = requests.get(gateway_url, **kwargs)
    response.raise_for_status()
    return response.status_code


def create_sms_log(args, sent_to):
    log = SMS_Log.objects.create(message=args['message'].decode(
        "utf-8"), sent_to=', '.join(sent_to))
    log.save()

build/lib/send_sms/views.py
hizbul25/django-send-sms
from django.contrib import admin
from django.http.request import HttpRequest

from send_sms.models import SMS_Log, SMSParameters, SMSSettings

# Register your models here.


class SMSParametersAdmin(admin.TabularInline):
    model = SMSParameters


class SMSSettingsAdmin(admin.ModelAdmin):
    inlines = [SMSParametersAdmin]

    def has_add_permission(self, request: HttpRequest) -> bool:
        return not SMSSettings.objects.exists()


@admin.register(SMS_Log)
class SMSLogAdmin(admin.ModelAdmin):
    list_display = ['sent_to', 'sent_on', 'message']
    readonly_fields = ['sent_to', 'sent_on', 'message']


admin.site.register(SMSSettings, SMSSettingsAdmin)

send_sms/admin.py
hizbul25/django-send-sms
from django.apps import AppConfig


class SendSmsConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'send_sms'

send_sms/apps.py
hizbul25/django-send-sms
# Generated by Django 4.2.5 on 2023-09-07 03:31

from django.db import migrations, models


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='SMS_Log',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('sent_on', models.DateTimeField(auto_now=True)),
                ('message', models.TextField()),
                ('sent_to', models.CharField(max_length=28)),
            ],
        ),
        migrations.CreateModel(
            name='SMSSettings',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('SMS Gateway URL', models.URLField(help_text='Eg. smsgateway.com/api/send_sms.cgi')),
                ('message_parameter', models.CharField(help_text='Enter url parameter for message', max_length=64)),
                ('receiver_parameter', models.CharField(help_text='Enter url parameter for receiver nos', max_length=64)),
                ('Static Parameters', models.JSONField(help_text='Enter static url parameters here (Eg. sender=django, username=django, password=1234 etc.)')),
                ('Use POST', models.BooleanField(default=False)),
            ],
        ),
    ]

send_sms/migrations/0001_initial.py
hizbul25/django-send-sms
# Generated by Django 4.2.5 on 2023-09-08 08:20

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    dependencies = [
        ('send_sms', '0001_initial'),
    ]

    operations = [
        migrations.RemoveField(
            model_name='smssettings',
            name='Static Parameters',
        ),
        migrations.CreateModel(
            name='SMSParameters',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('Parameter', models.CharField(max_length=128)),
                ('value', models.CharField(max_length=128)),
                ('header', models.BooleanField(default=False)),
                ('sms_settings', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='send_sms.smssettings')),
            ],
        ),
    ]

send_sms/migrations/0002_remove_smssettings_static_parameters_smsparameters.py
hizbul25/django-send-sms
# Generated by Django 4.2.5 on 2023-09-08 09:44

from django.db import migrations


class Migration(migrations.Migration):

    dependencies = [
        ('send_sms', '0002_remove_smssettings_static_parameters_smsparameters'),
    ]

    operations = [
        migrations.RenameField(
            model_name='smsparameters',
            old_name='Parameter',
            new_name='parameter',
        ),
        migrations.RenameField(
            model_name='smssettings',
            old_name='SMS Gateway URL',
            new_name='sms_gateway_url',
        ),
        migrations.RenameField(
            model_name='smssettings',
            old_name='Use POST',
            new_name='use_post',
        ),
    ]

send_sms/migrations/0003_rename_parameter_smsparameters_parameter_and_more.py
hizbul25/django-send-sms
from django.db import models

# Create your models here.


class SMSSettings(models.Model):
    sms_gateway_url = models.URLField(
        blank=False, help_text='Eg. smsgateway.com/api/send_sms.cgi')
    message_parameter = models.CharField(
        blank=False, help_text='Enter url parameter for message', max_length=64)
    receiver_parameter = models.CharField(
        blank=False, help_text='Enter url parameter for receiver nos', max_length=64)
    use_post = models.BooleanField(default=False)

    class Meta:
        verbose_name_plural = 'SMS Settings'

    def __str__(self) -> str:
        return 'SMS Settings'


class SMSParameters(models.Model):
    parameter = models.CharField(max_length=128, blank=False)
    value = models.CharField(max_length=128, blank=False)
    header = models.BooleanField(default=False)
    sms_settings = models.ForeignKey(SMSSettings, on_delete=models.CASCADE)
    
    def __str__(self) -> str:
        return self.parameter

class SMS_Log(models.Model):
    sent_on = models.DateTimeField(auto_now=True)
    message = models.TextField()
    sent_to = models.CharField(max_length=28)

    class Meta:
        verbose_name_plural = 'SMS Logs'

send_sms/models.py
hizbul25/django-send-sms
from django.core.exceptions import ValidationError
from django.test import TestCase

from send_sms.views import validate_receiver_nos

# Create your tests here.


class SMSTestCase(TestCase):
    def test_validate_receiver_nos(self):
        response = validate_receiver_nos(['01671589788', '01671589799'])
        self.assertEqual(len(response), 2)
        with self.assertRaises(ValidationError, msg='Please enter valid mobile nos'):
            validate_receiver_nos([''])

send_sms/tests.py
hizbul25/django-send-sms
from django.core.exceptions import ObjectDoesNotExist, ValidationError

from send_sms.models import SMS_Log, SMSParameters, SMSSettings

# Create your views here.


def validate_receiver_nos(receiver_list):
    validated_receiver_list = []
    for d in receiver_list:
        if not d:
            break

        # remove invalid character
        for x in [" ", "-", "(", ")"]:
            d = d.replace(x, "")

        validated_receiver_list.append(d)

    if not validated_receiver_list:
        raise ValidationError("Please enter valid mobile nos")

    return validated_receiver_list


def send_sms(receiver_list: list, msg: str, sender_name="", success_msg=True):

    import json

    if isinstance(receiver_list, str):
        receiver_list = json.loads(receiver_list)
        if not isinstance(receiver_list, list):
            receiver_list = [receiver_list]

    receiver_list = validate_receiver_nos(receiver_list)

    arg = {
        "receiver_list": receiver_list,
        "message": msg.encode("utf-8"),
        "success_msg": success_msg,
    }
    if SMSSettings.objects.first():
        send_via_gateway(arg)
    else:
        raise ObjectDoesNotExist("Please Update SMS Settings")


def send_via_gateway(arg):
    ss = SMSSettings.objects.first()
    headers = get_headers()
    use_json = headers.get("Content-Type") == "application/json"

    message = arg.get("message")
    args = {ss.message_parameter: message}
    parameters = SMSParameters.objects.filter(sms_settings=ss)
    for d in parameters:
        if not d.header:
            args[d.parameter] = d.value

    success_list = []
    for d in arg.get("receiver_list"):
        args[ss.receiver_parameter] = d
        status = send_request(ss.sms_gateway_url, args,
                              headers, ss.use_post, use_json)

        if 200 <= status < 300:
            success_list.append(d)

    if len(success_list) > 0:
        args.update(arg)
        create_sms_log(args, success_list)
        if arg.get("success_msg"):
            print(f"SMS sent to following numbers: {', '.join(success_list)}")


def get_headers(sms_settings=None):
    if not sms_settings:
        sms_settings = SMSSettings.objects.first()
        parameters = SMSParameters.objects.filter(sms_settings=sms_settings)

    headers = {"Accept": "text/plain, text/html, */*"}
    for d in parameters:
        if d.header == True:
            headers.update({d.parameter: d.value})

    return headers


def send_request(gateway_url, params, headers=None, use_post=False, use_json=False):
    import requests

    if not headers:
        headers = get_headers()
    kwargs = {"headers": headers}

    if use_json:
        kwargs["json"] = params
    elif use_post:
        kwargs["data"] = params
    else:
        kwargs["params"] = params

    if use_post:
        response = requests.post(gateway_url, **kwargs)
    else:
        response = requests.get(gateway_url, **kwargs)
    response.raise_for_status()
    return response.status_code


def create_sms_log(args, sent_to):
    log = SMS_Log.objects.create(message=args['message'].decode(
        "utf-8"), sent_to=', '.join(sent_to))
    log.save()

send_sms/views.py
hizbul25/django-send-sms
#!/usr/bin/env python
from setuptools import setup, find_packages

if __name__ == "__main__":
    long_description = "\n".join([
        open("README.rst").read(),
    ])
    
    setup(
        keywords=['SMS', 'SEND SMS', 'Universal SMS', 'Django SMS'],
        long_description=long_description,
        long_description_content_type='text/markdown',
        packages=find_packages(exclude=['media']) 
    )

setup.py
hizbul25/django-send-sms