django-chelseru 1.0.0__tar.gz → 1.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {django_chelseru-1.0.0/django_chelseru.egg-info → django_chelseru-1.0.1}/PKG-INFO +5 -3
- {django_chelseru-1.0.0 → django_chelseru-1.0.1/django_chelseru.egg-info}/PKG-INFO +5 -3
- django_chelseru-1.0.1/django_chelseru.egg-info/SOURCES.txt +27 -0
- {django_chelseru-1.0.0 → django_chelseru-1.0.1}/django_chelseru.egg-info/requires.txt +2 -0
- django_chelseru-1.0.1/django_chelseru.egg-info/top_level.txt +1 -0
- django_chelseru-1.0.1/drfchelseru/admin.py +24 -0
- django_chelseru-1.0.1/drfchelseru/apps.py +9 -0
- django_chelseru-1.0.1/drfchelseru/middlewares.py +44 -0
- django_chelseru-1.0.1/drfchelseru/migrations/0001_initial.py +33 -0
- django_chelseru-1.0.1/drfchelseru/migrations/0002_otpcode_session_user.py +92 -0
- django_chelseru-1.0.1/drfchelseru/migrations/0003_rename_mobile_otpcode_mobile_number.py +18 -0
- django_chelseru-1.0.1/drfchelseru/models.py +77 -0
- django_chelseru-1.0.1/drfchelseru/serializers.py +34 -0
- django_chelseru-1.0.1/drfchelseru/services.py +239 -0
- django_chelseru-1.0.1/drfchelseru/settings.py +174 -0
- django_chelseru-1.0.1/drfchelseru/signals.py +38 -0
- django_chelseru-1.0.1/drfchelseru/urls.py +11 -0
- django_chelseru-1.0.1/drfchelseru/validators.py +15 -0
- django_chelseru-1.0.1/drfchelseru/views.py +213 -0
- {django_chelseru-1.0.0 → django_chelseru-1.0.1}/setup.py +6 -4
- django_chelseru-1.0.0/django_chelseru.egg-info/SOURCES.txt +0 -36
- django_chelseru-1.0.0/django_chelseru.egg-info/top_level.txt +0 -3
- django_chelseru-1.0.0/drf_chelseru_auth/admin.py +0 -3
- django_chelseru-1.0.0/drf_chelseru_auth/apps.py +0 -6
- django_chelseru-1.0.0/drf_chelseru_auth/models.py +0 -3
- django_chelseru-1.0.0/drf_chelseru_auth/views.py +0 -3
- django_chelseru-1.0.0/drf_chelseru_chat/__init__.py +0 -0
- django_chelseru-1.0.0/drf_chelseru_chat/admin.py +0 -5
- django_chelseru-1.0.0/drf_chelseru_chat/apps.py +0 -6
- django_chelseru-1.0.0/drf_chelseru_chat/consumers.py +0 -82
- django_chelseru-1.0.0/drf_chelseru_chat/middleware.py +0 -33
- django_chelseru-1.0.0/drf_chelseru_chat/migrations/0001_initial.py +0 -36
- django_chelseru-1.0.0/drf_chelseru_chat/migrations/__init__.py +0 -0
- django_chelseru-1.0.0/drf_chelseru_chat/models.py +0 -23
- django_chelseru-1.0.0/drf_chelseru_chat/routing.py +0 -6
- django_chelseru-1.0.0/drf_chelseru_chat/serializers.py +0 -26
- django_chelseru-1.0.0/drf_chelseru_chat/urls.py +0 -12
- django_chelseru-1.0.0/drf_chelseru_chat/views.py +0 -59
- django_chelseru-1.0.0/drf_chelseru_sms/__init__.py +0 -0
- django_chelseru-1.0.0/drf_chelseru_sms/admin.py +0 -3
- django_chelseru-1.0.0/drf_chelseru_sms/apps.py +0 -6
- django_chelseru-1.0.0/drf_chelseru_sms/migrations/__init__.py +0 -0
- django_chelseru-1.0.0/drf_chelseru_sms/models.py +0 -3
- django_chelseru-1.0.0/drf_chelseru_sms/tests.py +0 -3
- django_chelseru-1.0.0/drf_chelseru_sms/views.py +0 -3
- {django_chelseru-1.0.0 → django_chelseru-1.0.1}/LICENSE +0 -0
- {django_chelseru-1.0.0 → django_chelseru-1.0.1}/MANIFEST.in +0 -0
- {django_chelseru-1.0.0 → django_chelseru-1.0.1}/README.md +0 -0
- {django_chelseru-1.0.0 → django_chelseru-1.0.1}/django_chelseru.egg-info/dependency_links.txt +0 -0
- {django_chelseru-1.0.0/drf_chelseru_auth → django_chelseru-1.0.1/drfchelseru}/__init__.py +0 -0
- {django_chelseru-1.0.0/drf_chelseru_auth → django_chelseru-1.0.1/drfchelseru}/migrations/__init__.py +0 -0
- {django_chelseru-1.0.0/drf_chelseru_auth → django_chelseru-1.0.1/drfchelseru}/tests.py +0 -0
- {django_chelseru-1.0.0 → django_chelseru-1.0.1}/pyproject.toml +0 -0
- {django_chelseru-1.0.0 → django_chelseru-1.0.1}/setup.cfg +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-chelseru
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.1
|
|
4
4
|
Summary: Authentication system, online and real-time chat, SMS system for Iranian SMS services.
|
|
5
5
|
Home-page: https://pip-django.chelseru.com
|
|
6
|
-
Author: Sobhan Bahman
|
|
6
|
+
Author: Sobhan Bahman|Rashnu
|
|
7
7
|
Author-email: bahmanrashnu@gmail.com
|
|
8
8
|
Project-URL: Documentation, https://github.com/Chelseru/django-chelseru-lour/
|
|
9
9
|
Project-URL: Telegram Group, https://t.me/bahmanpy
|
|
10
10
|
Project-URL: Telegram Channel, https://t.me/ChelseruCom
|
|
11
|
-
Keywords: djangochelseruchat djangochat drfchat online-chat online real-time chat iran chelseru lor lur bahman rashnu lour sms djangoiransms iransms djangosms djangokavenegar djangomelipayamak sobhan چت سبحان بهمن رشنو چلسرو جنگو پایتون لر لور آنلاین ریل تایم
|
|
11
|
+
Keywords: djangochelseruchat djangochat drfchat online-chat online real-time chat iran chelseru lor lur bahman rashnu lour sms djangoauth auth ywt otpauth otp authentication djangootp djangoiransms iransms djangosms djangokavenegar djangomelipayamak sobhan چت سبحان بهمن رشنو چلسرو جنگو پایتون لر لور آنلاین ریل تایم
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
13
|
Classifier: Framework :: Django
|
|
14
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -22,6 +22,8 @@ Requires-Dist: djangorestframework_simplejwt==5.5.0
|
|
|
22
22
|
Requires-Dist: channels==4.2.2
|
|
23
23
|
Requires-Dist: channels_redis==4.2.1
|
|
24
24
|
Requires-Dist: daphne==4.1.2
|
|
25
|
+
Requires-Dist: zeep==4.3.1
|
|
26
|
+
Requires-Dist: user-agents==2.2.0
|
|
25
27
|
Dynamic: author
|
|
26
28
|
Dynamic: author-email
|
|
27
29
|
Dynamic: classifier
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-chelseru
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.1
|
|
4
4
|
Summary: Authentication system, online and real-time chat, SMS system for Iranian SMS services.
|
|
5
5
|
Home-page: https://pip-django.chelseru.com
|
|
6
|
-
Author: Sobhan Bahman
|
|
6
|
+
Author: Sobhan Bahman|Rashnu
|
|
7
7
|
Author-email: bahmanrashnu@gmail.com
|
|
8
8
|
Project-URL: Documentation, https://github.com/Chelseru/django-chelseru-lour/
|
|
9
9
|
Project-URL: Telegram Group, https://t.me/bahmanpy
|
|
10
10
|
Project-URL: Telegram Channel, https://t.me/ChelseruCom
|
|
11
|
-
Keywords: djangochelseruchat djangochat drfchat online-chat online real-time chat iran chelseru lor lur bahman rashnu lour sms djangoiransms iransms djangosms djangokavenegar djangomelipayamak sobhan چت سبحان بهمن رشنو چلسرو جنگو پایتون لر لور آنلاین ریل تایم
|
|
11
|
+
Keywords: djangochelseruchat djangochat drfchat online-chat online real-time chat iran chelseru lor lur bahman rashnu lour sms djangoauth auth ywt otpauth otp authentication djangootp djangoiransms iransms djangosms djangokavenegar djangomelipayamak sobhan چت سبحان بهمن رشنو چلسرو جنگو پایتون لر لور آنلاین ریل تایم
|
|
12
12
|
Classifier: Programming Language :: Python :: 3
|
|
13
13
|
Classifier: Framework :: Django
|
|
14
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -22,6 +22,8 @@ Requires-Dist: djangorestframework_simplejwt==5.5.0
|
|
|
22
22
|
Requires-Dist: channels==4.2.2
|
|
23
23
|
Requires-Dist: channels_redis==4.2.1
|
|
24
24
|
Requires-Dist: daphne==4.1.2
|
|
25
|
+
Requires-Dist: zeep==4.3.1
|
|
26
|
+
Requires-Dist: user-agents==2.2.0
|
|
25
27
|
Dynamic: author
|
|
26
28
|
Dynamic: author-email
|
|
27
29
|
Dynamic: classifier
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
pyproject.toml
|
|
5
|
+
setup.py
|
|
6
|
+
django_chelseru.egg-info/PKG-INFO
|
|
7
|
+
django_chelseru.egg-info/SOURCES.txt
|
|
8
|
+
django_chelseru.egg-info/dependency_links.txt
|
|
9
|
+
django_chelseru.egg-info/requires.txt
|
|
10
|
+
django_chelseru.egg-info/top_level.txt
|
|
11
|
+
drfchelseru/__init__.py
|
|
12
|
+
drfchelseru/admin.py
|
|
13
|
+
drfchelseru/apps.py
|
|
14
|
+
drfchelseru/middlewares.py
|
|
15
|
+
drfchelseru/models.py
|
|
16
|
+
drfchelseru/serializers.py
|
|
17
|
+
drfchelseru/services.py
|
|
18
|
+
drfchelseru/settings.py
|
|
19
|
+
drfchelseru/signals.py
|
|
20
|
+
drfchelseru/tests.py
|
|
21
|
+
drfchelseru/urls.py
|
|
22
|
+
drfchelseru/validators.py
|
|
23
|
+
drfchelseru/views.py
|
|
24
|
+
drfchelseru/migrations/0001_initial.py
|
|
25
|
+
drfchelseru/migrations/0002_otpcode_session_user.py
|
|
26
|
+
drfchelseru/migrations/0003_rename_mobile_otpcode_mobile_number.py
|
|
27
|
+
drfchelseru/migrations/__init__.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
drfchelseru
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from django.contrib import admin
|
|
2
|
+
from .models import User, OTPCode, Session, Message
|
|
3
|
+
|
|
4
|
+
@admin.register(User)
|
|
5
|
+
class MobileAdmin(admin.ModelAdmin):
|
|
6
|
+
list_display = ['id', 'user__id', 'user__username', 'mobile', 'user__is_active']
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@admin.register(OTPCode)
|
|
10
|
+
class OTPCodeAdmin(admin.ModelAdmin):
|
|
11
|
+
list_display = ['id', 'code', 'mobile_number', 'created_at']
|
|
12
|
+
ordering = ('-created_at', )
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@admin.register(Session)
|
|
16
|
+
class SessionAdmin(admin.ModelAdmin):
|
|
17
|
+
list_display = ['id', 'user__id', 'user__username', 'ip_address', 'last_seen']
|
|
18
|
+
ordering = ('-created_at', )
|
|
19
|
+
|
|
20
|
+
@admin.register(Message)
|
|
21
|
+
class MessageAdmin(admin.ModelAdmin):
|
|
22
|
+
list_display = ['id', 'mobile_number', 'message_text', 'status']
|
|
23
|
+
ordering = ('-created_at', )
|
|
24
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from django.utils.timezone import datetime
|
|
2
|
+
from .models import Session
|
|
3
|
+
import user_agents
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TakeUserSessionMiddlaware:
|
|
7
|
+
def __init__(self, get_response):
|
|
8
|
+
self.get_response = get_response
|
|
9
|
+
|
|
10
|
+
def __call__(self, request):
|
|
11
|
+
response = self.get_response(request)
|
|
12
|
+
|
|
13
|
+
if request.user.is_authenticated:
|
|
14
|
+
user_agent = request.META.get('HTTP_USER_AGENT', '')
|
|
15
|
+
ip = self.get_client_ip(request)
|
|
16
|
+
|
|
17
|
+
if not request.session.session_key:
|
|
18
|
+
request.session.create()
|
|
19
|
+
|
|
20
|
+
session_key = request.session.session_key
|
|
21
|
+
|
|
22
|
+
session, created = Session.objects.get_or_create(
|
|
23
|
+
user = request.user,
|
|
24
|
+
session_key = session_key,
|
|
25
|
+
defaults = {
|
|
26
|
+
'user_agent': user_agent,
|
|
27
|
+
'ip_address': ip,
|
|
28
|
+
'device': user_agents.parse(user_agent).device.family,
|
|
29
|
+
'browser': user_agents.parse(user_agent).browser.family,
|
|
30
|
+
}
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
session.user_agent = user_agent
|
|
34
|
+
session.ip_address = ip
|
|
35
|
+
session.last_seen = datetime.now()
|
|
36
|
+
session.save()
|
|
37
|
+
|
|
38
|
+
return response
|
|
39
|
+
|
|
40
|
+
def get_client_ip(self, request):
|
|
41
|
+
x_forwarded_for = request.META.get('X_FORWARDED_FOR')
|
|
42
|
+
if x_forwarded_for:
|
|
43
|
+
return x_forwarded_for.split(',')[0]
|
|
44
|
+
return request.META.get('REMOTE_ADDR')
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Generated by Django 5.1.6 on 2025-08-12 01:59
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
initial = True
|
|
9
|
+
|
|
10
|
+
dependencies = []
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.CreateModel(
|
|
14
|
+
name="Message",
|
|
15
|
+
fields=[
|
|
16
|
+
(
|
|
17
|
+
"id",
|
|
18
|
+
models.BigAutoField(
|
|
19
|
+
auto_created=True,
|
|
20
|
+
primary_key=True,
|
|
21
|
+
serialize=False,
|
|
22
|
+
verbose_name="ID",
|
|
23
|
+
),
|
|
24
|
+
),
|
|
25
|
+
("message_text", models.TextField()),
|
|
26
|
+
("mobile_number", models.CharField(max_length=20)),
|
|
27
|
+
("_from", models.CharField(blank=True, max_length=20, null=True)),
|
|
28
|
+
("status", models.CharField(blank=True, max_length=20, null=True)),
|
|
29
|
+
("updated_at", models.DateTimeField(auto_now=True)),
|
|
30
|
+
("created_at", models.DateTimeField(auto_now_add=True)),
|
|
31
|
+
],
|
|
32
|
+
),
|
|
33
|
+
]
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Generated by Django 5.1.6 on 2025-08-12 02:29
|
|
2
|
+
|
|
3
|
+
import django.db.models.deletion
|
|
4
|
+
from django.conf import settings
|
|
5
|
+
from django.db import migrations, models
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Migration(migrations.Migration):
|
|
9
|
+
|
|
10
|
+
dependencies = [
|
|
11
|
+
("drfchelseru", "0001_initial"),
|
|
12
|
+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
operations = [
|
|
16
|
+
migrations.CreateModel(
|
|
17
|
+
name="OTPCode",
|
|
18
|
+
fields=[
|
|
19
|
+
(
|
|
20
|
+
"id",
|
|
21
|
+
models.BigAutoField(
|
|
22
|
+
auto_created=True,
|
|
23
|
+
primary_key=True,
|
|
24
|
+
serialize=False,
|
|
25
|
+
verbose_name="ID",
|
|
26
|
+
),
|
|
27
|
+
),
|
|
28
|
+
("code", models.CharField(max_length=10)),
|
|
29
|
+
("mobile", models.CharField(max_length=11)),
|
|
30
|
+
("created_at", models.DateTimeField(auto_now_add=True)),
|
|
31
|
+
],
|
|
32
|
+
),
|
|
33
|
+
migrations.CreateModel(
|
|
34
|
+
name="Session",
|
|
35
|
+
fields=[
|
|
36
|
+
(
|
|
37
|
+
"id",
|
|
38
|
+
models.BigAutoField(
|
|
39
|
+
auto_created=True,
|
|
40
|
+
primary_key=True,
|
|
41
|
+
serialize=False,
|
|
42
|
+
verbose_name="ID",
|
|
43
|
+
),
|
|
44
|
+
),
|
|
45
|
+
("session_key", models.CharField(max_length=40, unique=True)),
|
|
46
|
+
("user_agent", models.TextField()),
|
|
47
|
+
("ip_address", models.GenericIPAddressField()),
|
|
48
|
+
("device", models.TextField()),
|
|
49
|
+
("browser", models.TextField()),
|
|
50
|
+
("last_seen", models.DateTimeField(auto_now=True)),
|
|
51
|
+
("created_at", models.DateTimeField(auto_now_add=True)),
|
|
52
|
+
(
|
|
53
|
+
"user",
|
|
54
|
+
models.ForeignKey(
|
|
55
|
+
on_delete=django.db.models.deletion.DO_NOTHING,
|
|
56
|
+
related_name="session_drf_chelseru",
|
|
57
|
+
to=settings.AUTH_USER_MODEL,
|
|
58
|
+
),
|
|
59
|
+
),
|
|
60
|
+
],
|
|
61
|
+
),
|
|
62
|
+
migrations.CreateModel(
|
|
63
|
+
name="User",
|
|
64
|
+
fields=[
|
|
65
|
+
(
|
|
66
|
+
"id",
|
|
67
|
+
models.BigAutoField(
|
|
68
|
+
auto_created=True,
|
|
69
|
+
primary_key=True,
|
|
70
|
+
serialize=False,
|
|
71
|
+
verbose_name="ID",
|
|
72
|
+
),
|
|
73
|
+
),
|
|
74
|
+
("mobile", models.CharField(max_length=11)),
|
|
75
|
+
(
|
|
76
|
+
"group",
|
|
77
|
+
models.IntegerField(
|
|
78
|
+
default=0,
|
|
79
|
+
help_text="choice group type or user level, with numbers.",
|
|
80
|
+
),
|
|
81
|
+
),
|
|
82
|
+
(
|
|
83
|
+
"user",
|
|
84
|
+
models.OneToOneField(
|
|
85
|
+
on_delete=django.db.models.deletion.CASCADE,
|
|
86
|
+
related_name="mobile_drf_chelseru",
|
|
87
|
+
to=settings.AUTH_USER_MODEL,
|
|
88
|
+
),
|
|
89
|
+
),
|
|
90
|
+
],
|
|
91
|
+
),
|
|
92
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Generated by Django 5.1.6 on 2025-08-12 03:15
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
("drfchelseru", "0002_otpcode_session_user"),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.RenameField(
|
|
14
|
+
model_name="otpcode",
|
|
15
|
+
old_name="mobile",
|
|
16
|
+
new_name="mobile_number",
|
|
17
|
+
),
|
|
18
|
+
]
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
from django.contrib.auth.models import User as default_user
|
|
3
|
+
from django.utils.timezone import now, timedelta
|
|
4
|
+
from random import randint
|
|
5
|
+
from .settings import auth_init_check
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class User(models.Model):
|
|
9
|
+
user = models.OneToOneField(default_user, on_delete=models.CASCADE, related_name='mobile_drf_chelseru')
|
|
10
|
+
mobile = models.CharField(max_length=11)
|
|
11
|
+
group = models.IntegerField(default=0, help_text='choice group type or user level, with numbers.')
|
|
12
|
+
|
|
13
|
+
def __str__(self):
|
|
14
|
+
return f'{self.user.username} | {self.mobile}'
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class OTPCode(models.Model):
|
|
18
|
+
code = models.CharField(max_length=10)
|
|
19
|
+
mobile_number = models.CharField(max_length=11)
|
|
20
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
|
21
|
+
|
|
22
|
+
def __str__(self):
|
|
23
|
+
return f'{self.code} -> {self.mobile_number} | {self.created_at}'
|
|
24
|
+
|
|
25
|
+
def save(self, *args, **kwargs):
|
|
26
|
+
"""
|
|
27
|
+
Generates a new random code if one does not already exist.
|
|
28
|
+
"""
|
|
29
|
+
if not self.code:
|
|
30
|
+
icheck = auth_init_check()
|
|
31
|
+
if icheck and isinstance(icheck, dict):
|
|
32
|
+
otp_len = icheck['OPTIONS']['len']
|
|
33
|
+
otp_exp_time = icheck['OPTIONS']['exp_time']
|
|
34
|
+
|
|
35
|
+
self.code = str(randint(int('1' + (otp_len - 1) * '0'), int(otp_len * '9')))
|
|
36
|
+
super().save(*args, **kwargs)
|
|
37
|
+
|
|
38
|
+
def check_code(self):
|
|
39
|
+
try:
|
|
40
|
+
icheck = auth_init_check()
|
|
41
|
+
if icheck and isinstance(icheck, dict):
|
|
42
|
+
otp_exp_time = icheck['OPTIONS']['exp_time']
|
|
43
|
+
if now().timestamp() <= (self.created_at + timedelta(seconds=otp_exp_time * 60)).timestamp():
|
|
44
|
+
self.delete()
|
|
45
|
+
return True
|
|
46
|
+
self.delete()
|
|
47
|
+
except:
|
|
48
|
+
pass
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class Session(models.Model):
|
|
53
|
+
user = models.ForeignKey(default_user, models.DO_NOTHING, related_name='session_drf_chelseru')
|
|
54
|
+
session_key = models.CharField(max_length=40, unique=True)
|
|
55
|
+
user_agent = models.TextField()
|
|
56
|
+
ip_address = models.GenericIPAddressField()
|
|
57
|
+
device = models.TextField()
|
|
58
|
+
browser = models.TextField()
|
|
59
|
+
|
|
60
|
+
last_seen = models.DateTimeField(auto_now=True)
|
|
61
|
+
created_at = models.DateTimeField(auto_now_add = True)
|
|
62
|
+
|
|
63
|
+
def __str__(self):
|
|
64
|
+
return f'{self.user} - {self.ip_address}'
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class Message(models.Model):
|
|
68
|
+
message_text = models.TextField()
|
|
69
|
+
mobile_number = models.CharField(max_length=20)
|
|
70
|
+
_from = models.CharField(max_length=20, blank=True, null=True)
|
|
71
|
+
status = models.CharField(max_length=20, blank=True, null=True)
|
|
72
|
+
|
|
73
|
+
updated_at = models.DateTimeField(auto_now=True)
|
|
74
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
|
75
|
+
|
|
76
|
+
def __str__(self):
|
|
77
|
+
return f'to: {self.mobile_number} , at: {self.created_at}'
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from rest_framework.serializers import ModelSerializer
|
|
2
|
+
from django.contrib.auth.models import User
|
|
3
|
+
from .models import User as mobile, OTPCode, Session, Message
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DefaultUserSerializer(ModelSerializer):
|
|
7
|
+
class Meta:
|
|
8
|
+
model = User
|
|
9
|
+
fields = '__all__'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MobileSerializer(ModelSerializer):
|
|
13
|
+
class Meta:
|
|
14
|
+
model = mobile
|
|
15
|
+
fields = '__all__'
|
|
16
|
+
depth = 1
|
|
17
|
+
|
|
18
|
+
class OTPCodeSerializer(ModelSerializer):
|
|
19
|
+
class Meta:
|
|
20
|
+
model = OTPCode
|
|
21
|
+
fields = '__all__'
|
|
22
|
+
read_only_fields = ('code', )
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class SessionSerializer(ModelSerializer):
|
|
26
|
+
class Meta:
|
|
27
|
+
model = Session
|
|
28
|
+
fields = '__all__'
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class MessageSerializer(ModelSerializer):
|
|
32
|
+
class Meta:
|
|
33
|
+
model = Message
|
|
34
|
+
fields = '__all__'
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import requests, json
|
|
2
|
+
from django.conf import settings
|
|
3
|
+
from django.core.exceptions import ImproperlyConfigured
|
|
4
|
+
from zeep import Client
|
|
5
|
+
from rest_framework.status import HTTP_200_OK, HTTP_204_NO_CONTENT, HTTP_500_INTERNAL_SERVER_ERROR, HTTP_502_BAD_GATEWAY, HTTP_401_UNAUTHORIZED, HTTP_400_BAD_REQUEST
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
from .settings import sms_init_check
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ParsianWebcoIr:
|
|
12
|
+
"""
|
|
13
|
+
token
|
|
14
|
+
TemplateID
|
|
15
|
+
MessageVars
|
|
16
|
+
Receiver
|
|
17
|
+
delay
|
|
18
|
+
"""
|
|
19
|
+
API_KEY = None
|
|
20
|
+
HEADERS = {"Content-Type": "application/x-www-form-urlencoded"}
|
|
21
|
+
def __init__(self, mobile, options, *args, **kwargs):
|
|
22
|
+
self.RECEIVER = mobile
|
|
23
|
+
if options and 'api_key' in options:
|
|
24
|
+
self.API_KEY = options['api_key']
|
|
25
|
+
|
|
26
|
+
def send_message(self, message, template_id):
|
|
27
|
+
try:
|
|
28
|
+
api_url = 'https://api.parsianwebco.ir/webservice-send-sms/send'
|
|
29
|
+
data = {
|
|
30
|
+
'token': self.API_KEY,
|
|
31
|
+
'TemplateID': template_id,
|
|
32
|
+
'MessageVars': message,
|
|
33
|
+
'Receiver': self.RECEIVER,
|
|
34
|
+
'delay': 1
|
|
35
|
+
}
|
|
36
|
+
return json.loads(requests.post(url=api_url, data=data, headers=self.HEADERS).content)
|
|
37
|
+
"""
|
|
38
|
+
response:
|
|
39
|
+
status:
|
|
40
|
+
200 ok
|
|
41
|
+
100 faild
|
|
42
|
+
401 no authenticated
|
|
43
|
+
"""
|
|
44
|
+
except:
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class MeliPayamakCom:
|
|
49
|
+
'''
|
|
50
|
+
username
|
|
51
|
+
password
|
|
52
|
+
from
|
|
53
|
+
to
|
|
54
|
+
text
|
|
55
|
+
'''
|
|
56
|
+
USERNAME = None
|
|
57
|
+
PASSWORD = None
|
|
58
|
+
FROM = None
|
|
59
|
+
|
|
60
|
+
def __init__(self, mobile, options, *args, **kwargs):
|
|
61
|
+
self.RECEIVER = mobile
|
|
62
|
+
if options and 'username' in options and 'password' in options and 'from' in options:
|
|
63
|
+
self.USERNAME = options['username']
|
|
64
|
+
self.PASSWORD = options['password']
|
|
65
|
+
self.FROM = options['from']
|
|
66
|
+
|
|
67
|
+
def send_message(self, message):
|
|
68
|
+
try:
|
|
69
|
+
client = Client(wsdl='https://api.payamak-panel.com/post/Send.asmx?wsdl')
|
|
70
|
+
data = {
|
|
71
|
+
'username': self.USERNAME,
|
|
72
|
+
'password': self.PASSWORD,
|
|
73
|
+
'from': self.FROM,
|
|
74
|
+
'to': self.RECEIVER,
|
|
75
|
+
'text': message,
|
|
76
|
+
'isflash': False
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
response = client.service.SendSimpleSMS2(**data)
|
|
80
|
+
return response
|
|
81
|
+
"""
|
|
82
|
+
response:
|
|
83
|
+
status:
|
|
84
|
+
recld (Unique value for each successful submission)
|
|
85
|
+
0 The username or password is incorrect.
|
|
86
|
+
2 Not enough credit.
|
|
87
|
+
3 Limit on daily sending.
|
|
88
|
+
4 Limit on sending volume.
|
|
89
|
+
5 The sender's number is not valid.
|
|
90
|
+
6 The system is being updated.
|
|
91
|
+
7 The text contains the filtered word.
|
|
92
|
+
9 Sending from public lines via web service is not possible.
|
|
93
|
+
10 The desired user is not active.
|
|
94
|
+
11 Not sent.
|
|
95
|
+
12 The user's credentials are not complete.
|
|
96
|
+
14 The text contains a link.
|
|
97
|
+
15 Sending to more than 1 mobile number is not possible without inserting "لغو11".
|
|
98
|
+
16 No recipient number found
|
|
99
|
+
17 The text of the SMS is empty.
|
|
100
|
+
35 In REST, it means that the number is on the blacklist of communications.
|
|
101
|
+
"""
|
|
102
|
+
except:
|
|
103
|
+
return False
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class KavenegarCom:
|
|
107
|
+
'''
|
|
108
|
+
API_KEY
|
|
109
|
+
receptor
|
|
110
|
+
message
|
|
111
|
+
sender
|
|
112
|
+
'''
|
|
113
|
+
API_KEY = None
|
|
114
|
+
SENDER = None
|
|
115
|
+
|
|
116
|
+
def __init__(self, mobile, options, *args, **kwargs):
|
|
117
|
+
self.RECEIVER = mobile
|
|
118
|
+
if options and 'api_key' in options and 'from' in options:
|
|
119
|
+
self.API_KEY = options['api_key']
|
|
120
|
+
self.SENDER = options['from']
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def send_message(self, message):
|
|
124
|
+
try:
|
|
125
|
+
api_url = f'https://api.kavenegar.com/v1/{self.API_KEY}/sms/send.json'
|
|
126
|
+
data = {
|
|
127
|
+
'sender': self.SENDER,
|
|
128
|
+
'receptor': self.RECEIVER,
|
|
129
|
+
'message': message,
|
|
130
|
+
}
|
|
131
|
+
response = requests.post(url=api_url, data=data)
|
|
132
|
+
return response
|
|
133
|
+
"""
|
|
134
|
+
response:
|
|
135
|
+
messageid Unique identifier of this SMS (To know the status of the sent SMS, this value is the input to the Status method.)
|
|
136
|
+
status:
|
|
137
|
+
200 if status is 10 & 5 , message received.
|
|
138
|
+
400 The parameters are incomplete.
|
|
139
|
+
401 Account has been deactivated.
|
|
140
|
+
403 The API-Key identification code is not valid.
|
|
141
|
+
406 Empty mandatory parameters sent.
|
|
142
|
+
411 The recipient is invalid.
|
|
143
|
+
412 The sender is invalid.
|
|
144
|
+
413 The message is empty or the message length exceeds the allowed limit. The maximum length of the entire SMS text is 900 characters.
|
|
145
|
+
414 The request volume exceeds the allowed limit, sending SMS: maximum 200 records per call and status control: maximum 500 records per call
|
|
146
|
+
416 The originating service IP does not match the settings.
|
|
147
|
+
418 Your credit is not sufficient.
|
|
148
|
+
451 Excessive calls within a specific time period are restricted to IP addresses.
|
|
149
|
+
"""
|
|
150
|
+
except:
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def send_message(mobile_number, message_text, data, template_id=None):
|
|
156
|
+
try:
|
|
157
|
+
icheck = sms_init_check()
|
|
158
|
+
if not (icheck and isinstance(icheck, dict) and 'SMS_SERVICE' in icheck and 'OPTIONS' in icheck):
|
|
159
|
+
raise 'SMS service settings are not configured correctly.'
|
|
160
|
+
|
|
161
|
+
except ImproperlyConfigured as e:
|
|
162
|
+
print(f"Configuration Error: {e}")
|
|
163
|
+
raise
|
|
164
|
+
|
|
165
|
+
sms_service = icheck['SMS_SERVICE']
|
|
166
|
+
options = icheck['OPTIONS']
|
|
167
|
+
response_data = None
|
|
168
|
+
response_status_code = HTTP_500_INTERNAL_SERVER_ERROR
|
|
169
|
+
response_bool = False
|
|
170
|
+
|
|
171
|
+
if sms_service == 'PARSIAN_WEBCO_IR':
|
|
172
|
+
try:
|
|
173
|
+
if not template_id:
|
|
174
|
+
template_id = data.get('template_id')
|
|
175
|
+
if not template_id:
|
|
176
|
+
raise ImproperlyConfigured('template_id is required for the PARSIAN_WEBCO_IR service.')
|
|
177
|
+
|
|
178
|
+
service = ParsianWebcoIr(mobile=mobile_number, options=options)
|
|
179
|
+
response = service.send_message(message=message_text, template_id=template_id)
|
|
180
|
+
if isinstance(response, dict) and 'status' in response:
|
|
181
|
+
obj_status = response['status']
|
|
182
|
+
if response['status'] == 200:
|
|
183
|
+
response_data = {'receiver': mobile_number, 'message': message_text}
|
|
184
|
+
response_status_code = HTTP_200_OK
|
|
185
|
+
response_bool = True
|
|
186
|
+
elif response['status'] == 100:
|
|
187
|
+
response_data = {'details': 'The SMS service provider was unable to process the request.'}
|
|
188
|
+
response_status_code = HTTP_502_BAD_GATEWAY
|
|
189
|
+
response_bool = False
|
|
190
|
+
elif response['status'] == 401:
|
|
191
|
+
response_data = {'details': 'Authentication is not accepted, check your token...'}
|
|
192
|
+
response_status_code = HTTP_401_UNAUTHORIZED
|
|
193
|
+
response_bool = False
|
|
194
|
+
except ImproperlyConfigured as e:
|
|
195
|
+
print(f"Configuration Error: {e}")
|
|
196
|
+
raise
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
elif sms_service == 'MELI_PAYAMAK_COM':
|
|
200
|
+
service = MeliPayamakCom(mobile=mobile_number, options=options)
|
|
201
|
+
response = service.send_message(message=message_text)
|
|
202
|
+
obj_status = response
|
|
203
|
+
if response in [0, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 14, 15, 16, 17, 35]:
|
|
204
|
+
response_data = {'details': 'The SMS service provider was unable to process the request.', 'errorcode': response}
|
|
205
|
+
response_status_code = HTTP_502_BAD_GATEWAY
|
|
206
|
+
response_bool = False
|
|
207
|
+
else:
|
|
208
|
+
response_data = {'receiver': mobile_number, 'message': message_text, 'messageid': response}
|
|
209
|
+
response_status_code = HTTP_200_OK
|
|
210
|
+
response_bool = True
|
|
211
|
+
|
|
212
|
+
elif sms_service == 'KAVENEGAR_COM':
|
|
213
|
+
service = KavenegarCom(mobile=mobile_number, options=options)
|
|
214
|
+
try:
|
|
215
|
+
response = service.send_message(message=message_text)
|
|
216
|
+
response_json = response.json()
|
|
217
|
+
entries = response_json.get('entries', [])
|
|
218
|
+
_return = response_json.get('return', {})
|
|
219
|
+
|
|
220
|
+
if entries:
|
|
221
|
+
response_data = entries[0]
|
|
222
|
+
obj_status = response_data.get('status')
|
|
223
|
+
if response.status_code == 200 and response_data.get('status') in [5, 10]:
|
|
224
|
+
response_data = {'receiver': response_data.get('receptor'), 'message': response_data.get('message'), 'messageid': response_data.get('messageid')}
|
|
225
|
+
response_status_code = HTTP_200_OK
|
|
226
|
+
response_bool = True
|
|
227
|
+
else:
|
|
228
|
+
response_data = {'details': 'The SMS service provider was unable to process the request.', 'errorcode': response_data.get('status'), 'errortext': response_data.get('statustext'), 'message': response_data.get('message')}
|
|
229
|
+
response_status_code = HTTP_502_BAD_GATEWAY
|
|
230
|
+
response_bool = False
|
|
231
|
+
elif _return:
|
|
232
|
+
response_data = {'details': 'The SMS service provider was unable to process the request.', 'message': _return.get('message')}
|
|
233
|
+
response_status_code = HTTP_502_BAD_GATEWAY
|
|
234
|
+
response_bool = False
|
|
235
|
+
except (ValueError, KeyError, IndexError) as e:
|
|
236
|
+
obj_status = 502
|
|
237
|
+
return False, {'details': 'Invalid response structure.', 'error': str(e)}
|
|
238
|
+
|
|
239
|
+
return response_bool, {'data': response_data, 'obj_status': obj_status, 'status': response_status_code}
|