diff --git a/README.md b/README.md index 03296a3..02e8b06 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,9 @@ pip install django-universal-sms ```python INSTALLED_APPS = [ - 'send_sms', 'django.contrib.admin', '...', + 'send_sms', ] ``` diff --git a/README.rst b/README.rst index 03296a3..02e8b06 100644 --- a/README.rst +++ b/README.rst @@ -22,9 +22,9 @@ pip install django-universal-sms ```python INSTALLED_APPS = [ - 'send_sms', 'django.contrib.admin', '...', + 'send_sms', ] ``` diff --git a/build/lib/send_sms/__init__.py b/build/lib/send_sms/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/send_sms/admin.py b/build/lib/send_sms/admin.py new file mode 100644 index 0000000..9c20df5 --- /dev/null +++ b/build/lib/send_sms/admin.py @@ -0,0 +1,26 @@ +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) diff --git a/build/lib/send_sms/apps.py b/build/lib/send_sms/apps.py new file mode 100644 index 0000000..d619f25 --- /dev/null +++ b/build/lib/send_sms/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class SendSmsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'send_sms' diff --git a/build/lib/send_sms/migrations/0001_initial.py b/build/lib/send_sms/migrations/0001_initial.py new file mode 100644 index 0000000..2861587 --- /dev/null +++ b/build/lib/send_sms/migrations/0001_initial.py @@ -0,0 +1,34 @@ +# 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)), + ], + ), + ] diff --git a/build/lib/send_sms/migrations/0002_remove_smssettings_static_parameters_smsparameters.py b/build/lib/send_sms/migrations/0002_remove_smssettings_static_parameters_smsparameters.py new file mode 100644 index 0000000..4f22aa1 --- /dev/null +++ b/build/lib/send_sms/migrations/0002_remove_smssettings_static_parameters_smsparameters.py @@ -0,0 +1,28 @@ +# 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')), + ], + ), + ] diff --git a/build/lib/send_sms/migrations/0003_rename_parameter_smsparameters_parameter_and_more.py b/build/lib/send_sms/migrations/0003_rename_parameter_smsparameters_parameter_and_more.py new file mode 100644 index 0000000..2caaf0e --- /dev/null +++ b/build/lib/send_sms/migrations/0003_rename_parameter_smsparameters_parameter_and_more.py @@ -0,0 +1,28 @@ +# 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', + ), + ] diff --git a/build/lib/send_sms/migrations/__init__.py b/build/lib/send_sms/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/build/lib/send_sms/models.py b/build/lib/send_sms/models.py new file mode 100644 index 0000000..1bf8a0c --- /dev/null +++ b/build/lib/send_sms/models.py @@ -0,0 +1,37 @@ +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' diff --git a/build/lib/send_sms/tests.py b/build/lib/send_sms/tests.py new file mode 100644 index 0000000..481fae5 --- /dev/null +++ b/build/lib/send_sms/tests.py @@ -0,0 +1,14 @@ +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(['']) diff --git a/build/lib/send_sms/views.py b/build/lib/send_sms/views.py new file mode 100644 index 0000000..3a7838e --- /dev/null +++ b/build/lib/send_sms/views.py @@ -0,0 +1,114 @@ +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() diff --git a/dist/django-universal-sms-1.0.1.tar.gz b/dist/django-universal-sms-1.0.1.tar.gz deleted file mode 100644 index e398077..0000000 Binary files a/dist/django-universal-sms-1.0.1.tar.gz and /dev/null differ diff --git a/dist/django-universal-sms-1.0.2.tar.gz b/dist/django-universal-sms-1.0.2.tar.gz deleted file mode 100644 index ec8967b..0000000 Binary files a/dist/django-universal-sms-1.0.2.tar.gz and /dev/null differ diff --git a/dist/django-universal-sms-1.0.3.tar.gz b/dist/django-universal-sms-1.0.3.tar.gz deleted file mode 100644 index f0549d0..0000000 Binary files a/dist/django-universal-sms-1.0.3.tar.gz and /dev/null differ diff --git a/dist/django-universal-sms-1.0.4.tar.gz b/dist/django-universal-sms-1.0.4.tar.gz deleted file mode 100644 index bb05d16..0000000 Binary files a/dist/django-universal-sms-1.0.4.tar.gz and /dev/null differ diff --git a/dist/django-universal-sms-1.0.6.tar.gz b/dist/django-universal-sms-1.0.6.tar.gz new file mode 100644 index 0000000..92fff38 Binary files /dev/null and b/dist/django-universal-sms-1.0.6.tar.gz differ diff --git a/dist/django_universal_sms-1.0.6-py3-none-any.whl b/dist/django_universal_sms-1.0.6-py3-none-any.whl new file mode 100644 index 0000000..57f69f6 Binary files /dev/null and b/dist/django_universal_sms-1.0.6-py3-none-any.whl differ diff --git a/django_universal_sms.egg-info/PKG-INFO b/django_universal_sms.egg-info/PKG-INFO index a0da574..5097546 100644 --- a/django_universal_sms.egg-info/PKG-INFO +++ b/django_universal_sms.egg-info/PKG-INFO @@ -1,6 +1,6 @@ -Metadata-Version: 1.2 +Metadata-Version: 2.1 Name: django-universal-sms -Version: 1.0.4 +Version: 1.0.6 Summary: Send SMS using Django Home-page: https://github.com/hizbul25/django-send-sms Author: Hizbul Bahar @@ -8,9 +8,7 @@ Author-email: hizbul.ku@gmail.com Maintainer: Hizbul Bahar Maintainer-email: hizbul.ku@gmail.com License: MIT -Description: README.rst Keywords: SMS,SEND SMS,Universal SMS,Django SMS -Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers @@ -29,3 +27,67 @@ Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Topic :: Software Development :: Libraries :: Application Frameworks Classifier: Topic :: Software Development :: Libraries :: Python Modules Requires-Python: >=3.6 +Description-Content-Type: text/markdown +License-File: LICENSE.txt +Requires-Dist: Django>=2.2 +Requires-Dist: requests>=2.2.0 + +## django-universal-sms + +Send SMS from Django application using any SMS service provider just writing a single line of code. + +## Features + +- Support all standard SMS service provider +- SMS configuration from django admin panel +- There is SMS Log listing page +- You can also test SMS sending from admin panel +- Enough test coverage + +## Installation + +### pip + +```bash +pip install django-universal-sms +``` + +## Install apps in `settings.py` + +```python +INSTALLED_APPS = [ + 'django.contrib.admin', + '...', + 'send_sms', +] +``` + +### migration + +```bash +python manage.py migrate +``` + +## Configuration + +- Go to admin panel by login with you admin credentials +- Find out **SMS Settings** menu under **SEND SMS** and click on it. +- There you'll see couple of fields to fill it up using SMS service provider credentials. + ![Here is the example](/media/SMS_Settings.png). +- Carfefully compelete this part and save it. + +## Usage + +Whenever you need to send SMS just use following code: + +```bash +from send_sms.views import send_sms + +send_sms(['mobile_number'], 'Your Message') +``` + +For each successful sending SMS you can see the log from **SMS Log** under under **SEND SMS** menu. + +## Support + +Feel free to ask for the support over the email hizbul.ku[at]gmail.com. diff --git a/django_universal_sms.egg-info/SOURCES.txt b/django_universal_sms.egg-info/SOURCES.txt index 46b547b..6c2d443 100644 --- a/django_universal_sms.egg-info/SOURCES.txt +++ b/django_universal_sms.egg-info/SOURCES.txt @@ -1,3 +1,4 @@ +LICENSE.txt MANIFEST.in README.rst setup.cfg @@ -6,4 +7,14 @@ django_universal_sms.egg-info/PKG-INFO django_universal_sms.egg-info/SOURCES.txt django_universal_sms.egg-info/dependency_links.txt django_universal_sms.egg-info/requires.txt -django_universal_sms.egg-info/top_level.txt \ No newline at end of file +django_universal_sms.egg-info/top_level.txt +send_sms/__init__.py +send_sms/admin.py +send_sms/apps.py +send_sms/models.py +send_sms/tests.py +send_sms/views.py +send_sms/migrations/0001_initial.py +send_sms/migrations/0002_remove_smssettings_static_parameters_smsparameters.py +send_sms/migrations/0003_rename_parameter_smsparameters_parameter_and_more.py +send_sms/migrations/__init__.py \ No newline at end of file diff --git a/django_universal_sms.egg-info/top_level.txt b/django_universal_sms.egg-info/top_level.txt index 8b13789..879f420 100644 --- a/django_universal_sms.egg-info/top_level.txt +++ b/django_universal_sms.egg-info/top_level.txt @@ -1 +1 @@ - +send_sms diff --git a/setup.cfg b/setup.cfg index 806d756..77a0f13 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,14 +1,12 @@ # setup.cfg [metadata] name = django-universal-sms -version = 1.0.4 +version = 1.0.6 author = Hizbul Bahar author_email = hizbul.ku@gmail.com maintainer = Hizbul Bahar maintainer_email = hizbul.ku@gmail.com description = Send SMS using Django -description-file = README.md -long_description = README.rst url = https://github.com/hizbul25/django-send-sms license = MIT classifiers = diff --git a/setup.py b/setup.py index 4615926..45d1094 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,14 @@ #!/usr/bin/env python +from setuptools import setup, find_packages if __name__ == "__main__": - import setuptools long_description = "\n".join([ open("README.rst").read(), ]) - setuptools.setup( - keywords=['SMS', 'SEND SMS', 'Universal SMS', 'Django SMS'] + + setup( + keywords=['SMS', 'SEND SMS', 'Universal SMS', 'Django SMS'], + long_description=long_description, + long_description_content_type='text/markdown', + packages=find_packages(exclude=['media']) )