django-chelseru 1.0.2__py3-none-any.whl → 1.0.4__py3-none-any.whl

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.
@@ -0,0 +1,316 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-chelseru
3
+ Version: 1.0.4
4
+ Summary: Authentication system, online and real-time chat, SMS system for Iranian SMS services.
5
+ Home-page: https://pipdjango.chelseru.com
6
+ Author: Sobhan Bahman|Rashnu
7
+ Author-email: bahmanrashnu@gmail.com
8
+ Project-URL: Documentation, https://github.com/Chelseru/django-chelseru-lour/
9
+ Project-URL: Telegram Group, https://t.me/bahmanpy
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 djangoauth auth ywt otpauth otp authentication djangootp djangoiransms iransms djangosms djangokavenegar djangomelipayamak sobhan چت سبحان بهمن رشنو چلسرو جنگو پایتون لر لور آنلاین ریل تایم
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Framework :: Django
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Requires-Python: >=3.11
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: Django>=5.1.6
20
+ Requires-Dist: djangorestframework==3.15.2
21
+ Requires-Dist: djangorestframework_simplejwt==5.5.0
22
+ Requires-Dist: channels==4.2.2
23
+ Requires-Dist: channels_redis==4.2.1
24
+ Requires-Dist: daphne==4.1.2
25
+ Requires-Dist: zeep==4.3.1
26
+ Requires-Dist: user-agents==2.2.0
27
+ Dynamic: author
28
+ Dynamic: author-email
29
+ Dynamic: classifier
30
+ Dynamic: description
31
+ Dynamic: description-content-type
32
+ Dynamic: home-page
33
+ Dynamic: keywords
34
+ Dynamic: license-file
35
+ Dynamic: project-url
36
+ Dynamic: requires-dist
37
+ Dynamic: requires-python
38
+ Dynamic: summary
39
+
40
+ django-chelseru
41
+ یک بسته جنگویی برای گپ‌زنی همزمان، راستی‌آزمایی پیامکی و فرستادن پیامک با یاری‌دهنده‌های ایرانی.
42
+
43
+ نویسنده
44
+ Sobhan Bahman Rashnu
45
+
46
+ 🚀 ویژگی‌ها
47
+ 📱 راستی‌آزمایی پیامکی (رمز یک‌بارمصرف): راستی‌آزمایی امن کاربران با یاری‌گیری از رمزهای یک‌بارمصرف که از راه پیامک فرستاده می‌شوند.
48
+
49
+ 💬 گپ‌زنی همزمان: کارکرد پیام‌رسانی همزمان بر پایه WebSocket.
50
+
51
+ ✉️ سامانه‌های پیامکی: فرستادن پیامک از راه یاری‌دهنده‌های نامور پیامکی ایرانی.
52
+
53
+ ⚙️ نصب
54
+ بسته را با یاری‌گیری از pip نصب کنید:
55
+
56
+ pip install django-chelseru
57
+
58
+ 'drfchelseru' را به INSTALLED_APPS در پرونده settings.py خود بیفزایید:
59
+
60
+ INSTALLED_APPS = [
61
+ ...
62
+ 'channels',
63
+ 'rest_framework',
64
+ 'rest_framework_simplejwt',
65
+ 'drfchelseru',
66
+ ...
67
+ ]
68
+
69
+ 🛠️ پیکربندی
70
+ برای پیکربندی بسته، واژه‌نامه DJANGO_CHELSERU را به پرونده settings.py خود بیفزایید. این واژه‌نامه به شما پروانه می‌دهد تا چیدمان‌های راستی‌آزمایی و پیامک را خودساخته نمایید.
71
+
72
+ # settings.py
73
+
74
+ DJANGO_CHELSERU = {
75
+ 'AUTH': {
76
+ 'AUTH_METHOD' : 'OTP', # روش‌های پشتیبانی شده: OTP, PASSWD
77
+ 'AUTH_SERVICE' : 'rest_framework_simplejwt', # سرویس‌های پشتیبانی شده: rest_framework_simplejwt
78
+ 'OPTIONS': {
79
+ 'OTP_LENGTH' : 8, # پیش‌فرض: 8
80
+ 'OTP_EXPIRE_PER_MINUTES': 4, # پیش‌فرض: 4
81
+ 'OTP_SMS_TEMPLATE_ID' : 1, # شناسه قالب پیامکی برای رمز یک‌بارمصرف
82
+ }
83
+ },
84
+ 'SMS': {
85
+ 'SMS_SERVICE': 'PARSIAN_WEBCO_IR', # یاری‌دهنده‌های پشتیبانی شده: PARSIAN_WEBCO_IR, MELI_PAYAMAK_COM, KAVENEGAR_COM
86
+ 'SETTINGS': {
87
+ 'PARSIAN_WEBCO_IR_API_KEY' : '',
88
+ 'MELI_PAYAMAK_COM_USERNAME' : '',
89
+ 'MELI_PAYAMAK_COM_PASSWORD' : '',
90
+ 'MELI_PAYAMAK_COM_FROM' : '',
91
+ 'KAVENEGAR_COM_API_KEY' : 'YOUR_KAVENEGAR_API_KEY',
92
+ 'KAVENEGAR_COM_FROM' : 'YOUR_KAVENEGAR_FROM_NUMBER',
93
+ },
94
+ 'TEMPLATES': {
95
+ 'T1': 1,
96
+ 'T2': 2,
97
+ ...
98
+ }
99
+ }
100
+ }
101
+
102
+ AUTH_METHOD: روش راستی‌آزمایی را روشن می‌سازد. برای راستی‌آزمایی پیامکی، از 'OTP' بهره بگیرید.
103
+
104
+ OTP_LENGTH: درازای رمز یک‌بارمصرف.
105
+
106
+ OTP_EXPIRE_PER_MINUTES: زمان پایان‌یافتن رمز یک‌بارمصرف بر پایه دقیقه.
107
+
108
+ OTP_SMS_TEMPLATE_ID: شناسه قالب پیامکی که برای فرستادن رمز یک‌بارمصرف بهره گرفته می‌شود.
109
+
110
+ SMS_SERVICE: یاری‌دهنده پیامکی دلخواه خود را برگزینید.
111
+
112
+ SETTINGS: آگاهی‌های نیاز برای یاری‌دهنده پیامکی برگزیده‌تان را فراهم آورید.
113
+
114
+ TEMPLATES: شناسه‌های قالب پیامکی خود را روشن سازید.
115
+
116
+ 🔌 نقطه‌های پایانی
117
+ برای به کارگیری کارکردهای این بسته، URLهای زیر را به پرونده urls.py خود بیفزایید.
118
+
119
+ # urls.py
120
+
121
+ from django.urls import path, include
122
+
123
+ urlpatterns = [
124
+ ...
125
+ path('api/', include('drfchelseru.urls')),
126
+ ...
127
+ ]
128
+
129
+ این بسته نقطه‌های پایانی API زیر را فراهم می‌آورد:
130
+
131
+ نقطه‌ پایانی
132
+
133
+ شرح
134
+
135
+ روش
136
+
137
+ /api/otp/send/
138
+
139
+ یک رمز یک‌بارمصرف به شماره همراه گفته‌شده می‌فرستد.
140
+
141
+ POST
142
+
143
+ /api/authenticate/
144
+
145
+ کاربری را با رمز یک‌بارمصرف دریافت‌شده، راستی‌آزمایی می‌کند.
146
+
147
+ POST
148
+
149
+ /api/sessions/
150
+
151
+ نشست‌های فعال کاربر را فهرست کرده و درایوری می‌کند.
152
+
153
+ GET
154
+
155
+ /api/message/send/
156
+
157
+ یک پیامک با یاری‌دهنده پیکربندی‌شده می‌فرستد.
158
+
159
+ POST
160
+
161
+ به کارگیری نقطه‌های پایانی
162
+ 1. فرستادن رمز یک‌بارمصرف (/api/otp/send/)
163
+ روش: POST شرح: یک رمز یک‌بارمصرف به شماره همراه کاربر می‌فرستد.
164
+
165
+ داده‌نماهای نیاز:
166
+
167
+ داده‌نما
168
+
169
+ گونه
170
+
171
+ شرح
172
+
173
+ نمونه
174
+
175
+ mobile_number
176
+
177
+ str
178
+
179
+ شماره همراه کاربر.
180
+
181
+ 09121234567
182
+
183
+ پاسخ‌ها:
184
+
185
+ HTTP 200 OK: رمز یک‌بارمصرف با کامیابی فرستاده شد.
186
+
187
+ {"details": "The OTP code was sent correctly."}
188
+
189
+ HTTP 400 BAD REQUEST: ساختار نادرست mobile_number.
190
+
191
+ HTTP 409 CONFLICT: یک رمز یک‌بارمصرف پیش‌تر فرستاده شده و هنوز روایی دارد.
192
+
193
+ {"details": "An OTP code has already been sent. Please wait X seconds before trying again."}
194
+
195
+ HTTP 500 INTERNAL SERVER ERROR: یک دشواری در کارگذار پیش آمده است.
196
+
197
+ 2. راستی‌آزمایی (/api/authenticate/)
198
+ روش: POST شرح: کاربر را با رمز یک‌بارمصرف فراهم شده، راستی‌آزمایی می‌کند. اگر با کامیابی انجام شود، توکن‌های JWT (access و refresh) را بازمی‌گرداند.
199
+
200
+ داده‌نماهای نیاز:
201
+
202
+ داده‌نما
203
+
204
+ گونه
205
+
206
+ شرح
207
+
208
+ نمونه
209
+
210
+ mobile_number
211
+
212
+ str
213
+
214
+ شماره همراه کاربر.
215
+
216
+ 09121234567
217
+
218
+ code
219
+
220
+ str
221
+
222
+ رمز یک‌بارمصرف دریافت شده از راه پیامک.
223
+
224
+ 12345678
225
+
226
+ group
227
+
228
+ int
229
+
230
+ اختیاری: یک شناسه دسته برای کاربر.
231
+
232
+ 1
233
+
234
+ پاسخ‌ها:
235
+
236
+ HTTP 200 OK: راستی‌آزمایی با کامیابی انجام شد.
237
+
238
+ {
239
+ "access": "...",
240
+ "refresh": "..."
241
+ }
242
+
243
+ HTTP 401 UNAUTHORIZED: رمز یک‌بارمصرف ناروا یا پایان‌یافته.
244
+
245
+ {"error": "The code sent to this mobile number was not found."}
246
+
247
+ HTTP 400 BAD REQUEST: داده‌نماهای نیاز ناپیدا یا ساختار نادرست.
248
+
249
+ HTTP 500 INTERNAL SERVER ERROR: یک دشواری در کارگذار پیش آمده است.
250
+
251
+ 3. فرستادن پیامک (/api/message/send/)
252
+ روش: POST شرح: یک پیامک خودساخته را با یاری‌دهنده پیکربندی‌شده می‌فرستد.
253
+
254
+ داده‌نماهای نیاز:
255
+
256
+ داده‌نما
257
+
258
+ گونه
259
+
260
+ شرح
261
+
262
+ نمونه
263
+
264
+ mobile_number
265
+
266
+ str
267
+
268
+ شماره همراه گیرنده.
269
+
270
+ 09121234567
271
+
272
+ message_text
273
+
274
+ str
275
+
276
+ نوشتار پیام. (بیشینه ۲۹۰ نویسه)
277
+
278
+ Hello, World!
279
+
280
+ template_id
281
+
282
+ int
283
+
284
+ برای برخی یاری‌دهنده‌ها (برای نمونه پارسیان) نیاز است.
285
+
286
+ 1
287
+
288
+ پاسخ‌ها:
289
+
290
+ HTTP 200 OK: پیام با کامیابی فرستاده شد.
291
+
292
+ {"details": "The Message was sent correctly."}
293
+
294
+ HTTP 400 BAD REQUEST: دشواری‌های درست‌سنجی برای داده‌نماها.
295
+
296
+ HTTP 401 UNAUTHORIZED: راستی‌آزمایی انجام نشد.
297
+
298
+ HTTP 500 INTERNAL SERVER ERROR: یک دشواری در کارگذار پیش آمده است.
299
+
300
+ HTTP 502 BAD GATEWAY: یاری‌دهنده پیامکی یک دشواری را بازگرداند.
301
+
302
+ 4. فهرست نشست‌ها (/api/sessions/)
303
+ روش: GET شرح: همه نشست‌های فعال کاربران را فهرست می‌کند. نیاز به راستی‌آزمایی (IsAuthenticated) دارد.
304
+
305
+ سربرگ‌های نیاز:
306
+
307
+ سربرگ
308
+
309
+ ارزش
310
+
311
+ Authorization
312
+
313
+ Bearer <your_access_token>
314
+
315
+ 💡 مدل‌ها
316
+ این بسته یک مدل Session برای درایوری نشست‌های فعال کاربران دارد. می‌توانید به این نشست‌ها از راه نقطه‌ پایانی /api/sessions/ دسترسی یافته و آنها را درایوری کنید.
@@ -0,0 +1,31 @@
1
+ django_chelseru-1.0.4.dist-info/licenses/LICENSE,sha256=VupU5KV4NteHaNQb-WH31G_WZWezxXoomjiCIAHoQJo,1089
2
+ drfchelseru/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ drfchelseru/admin.py,sha256=8X67Sy8k5qQzZ4ifuJ_5O-UqVWby4uX53Iq-KNuODxM,948
4
+ drfchelseru/apps.py,sha256=hOTTzFGLXiTPZeN8p_LLcSECLtsR2Q0SUo8zJzgM-qQ,211
5
+ drfchelseru/consumers.py,sha256=x64hPCd5FViFsFMyDIcxPOh2OWVpeNq1BaOdhYk0HUI,2620
6
+ drfchelseru/middlewares.py,sha256=adrhoJE7uo4WISfSIXK41XMQQzMaBshq3r61RblMPAQ,2570
7
+ drfchelseru/models.py,sha256=O7kYTC3TcjBvuxBqYccWZFx_JCOoQgL3jJ5zqi99-nc,6315
8
+ drfchelseru/routing.py,sha256=SEWMBmRFZs1NkKLuppXpmvRmyWiTY3KVIHmA4EpUyyI,167
9
+ drfchelseru/serializers.py,sha256=tM7dN362Vdx0UXq6OfNmYh6rTN1bwFoEgOn1MG4CZIY,1299
10
+ drfchelseru/services.py,sha256=FkJlsFtTSiwwQ6g-nkSuV8XvY-dX-abAY_0WGbD0Ppw,10139
11
+ drfchelseru/settings.py,sha256=ksiuFgjf1HZC6dajQXEra67WyUH6vYqjcQT81NdhELw,9051
12
+ drfchelseru/signals.py,sha256=nmFv208GW9nir9PAB0a1050OMh4I9em0n60O6Mgfwec,2554
13
+ drfchelseru/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2nk6VY-g,60
14
+ drfchelseru/urls.py,sha256=2RllsBPgojGLxLkyaBsSGk7fU1q44nuA7I_VpeHCmbs,702
15
+ drfchelseru/validators.py,sha256=jAJASfG4kPcqrGxxIcoR8VjS4L4n_EzZuxJtQ1g9qmo,480
16
+ drfchelseru/views.py,sha256=9m0lNPQBBbw5cl_WLmWL_lf3DHRvp9v45AmiUYLviSE,12620
17
+ drfchelseru/migrations/0001_initial.py,sha256=LfkWAeQuPXNW3_igM-w_pyKafCeHQF3aReYgMXfMzEE,1034
18
+ drfchelseru/migrations/0002_otpcode_session_user.py,sha256=c3oyBTQo2kjsKr5r_XQrqaq-66F7pui-4H4mEyAXgJk,3126
19
+ drfchelseru/migrations/0003_rename_mobile_otpcode_mobile_number.py,sha256=eiD7t6etg6nOeT7BhJrSfEcR1yTPePXtATgtyu30520,377
20
+ drfchelseru/migrations/0004_rename_message_message_sms.py,sha256=FxsPAtgioB-Ei6cZFj7B0E1DpBDiYJvqKtom9YiXknM,357
21
+ drfchelseru/migrations/0005_rename_message_sms_messagesms_chatroom_messagechat.py,sha256=ZJfFmeF4pXuhDSdz8ilOxXEBujEhnFQCAv564Dh4CGc,1586
22
+ drfchelseru/migrations/0006_alter_chatroom_user_1_alter_chatroom_user_2_and_more.py,sha256=m7ew_V1jXVPOVkKZO7WoWS473hMRZU_NwfGDnU_ZN4o,1394
23
+ drfchelseru/migrations/0007_chatroom_pinned_for_chatroom_status_chatroom_users_and_more.py,sha256=GRY3_oOuwCNyqXySIktKn7FiJ20ZBdbO4igxf-N3IbY,4532
24
+ drfchelseru/migrations/0008_alter_chatroompermissions_user.py,sha256=d-LTEpAJhDlhCt895eNRPu9iXzveHqp5oWtgRRL3WhE,661
25
+ drfchelseru/migrations/0009_alter_chatroom_status.py,sha256=QaAEUpERV8rnN-ifimA-HfQ8yLk4Gc8T6kER_DtBznk,488
26
+ drfchelseru/migrations/0010_chatroom_banneds_chatroom_descriptions_chatroom_name_and_more.py,sha256=aeotKPmdFJSjeA2-9VGApLfpx-NJkOrRJI2j2mdkf1U,1198
27
+ drfchelseru/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
+ django_chelseru-1.0.4.dist-info/METADATA,sha256=vLRe7Nw5ZhSktAeHRYC-rJh4oyeWQxbEaPhUebsugOo,9671
29
+ django_chelseru-1.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
30
+ django_chelseru-1.0.4.dist-info/top_level.txt,sha256=fsaO1F03W3j4AYi0TfDGv5Cjb_Qrh6RSkwkWqfqaMns,12
31
+ django_chelseru-1.0.4.dist-info/RECORD,,
drfchelseru/admin.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from django.contrib import admin
2
- from .models import User, OTPCode, Session, Message
2
+ from .models import User, OTPCode, Session, MessageSMS, ChatRoom, MessageChat, ChatRoomPermissions, Organization
3
+
3
4
 
4
5
  @admin.register(User)
5
6
  class MobileAdmin(admin.ModelAdmin):
@@ -17,8 +18,16 @@ class SessionAdmin(admin.ModelAdmin):
17
18
  list_display = ['id', 'user__id', 'user__username', 'ip_address', 'last_seen']
18
19
  ordering = ('-created_at', )
19
20
 
20
- @admin.register(Message)
21
+ @admin.register(MessageSMS)
21
22
  class MessageAdmin(admin.ModelAdmin):
22
23
  list_display = ['id', 'mobile_number', 'message_text', 'status']
23
24
  ordering = ('-created_at', )
24
25
 
26
+
27
+
28
+
29
+
30
+ admin.site.register(ChatRoom)
31
+ admin.site.register(MessageChat)
32
+ admin.site.register(ChatRoomPermissions)
33
+ admin.site.register(Organization)
@@ -0,0 +1,84 @@
1
+ import json
2
+ from channels.generic.websocket import AsyncWebsocketConsumer
3
+ from .models import ChatRoom, MessageChat
4
+ from django.contrib.auth import get_user_model
5
+ from asgiref.sync import sync_to_async
6
+
7
+
8
+ User = get_user_model()
9
+
10
+ class ChatConsumer(AsyncWebsocketConsumer):
11
+ @sync_to_async
12
+ def is_user_in_chat_room(self, user, chat_room):
13
+ if chat_room.users.filter(user__id=user.id).exists():
14
+ return True
15
+ return user == chat_room.user_1 or user == chat_room.user_2
16
+
17
+ async def connect(self):
18
+ user = self.scope["user"]
19
+ if user.is_authenticated:
20
+ self.user = user
21
+ self.chat_room_id = self.scope['url_route']['kwargs']['chat_room_id']
22
+ self.chat_room = await sync_to_async(ChatRoom.objects.get)(id=self.chat_room_id)
23
+
24
+ if not await self.is_user_in_chat_room(user, self.chat_room):
25
+ await self.close()
26
+ return
27
+
28
+ self.room_group_name = f"chat_{self.chat_room.id}"
29
+
30
+ # Join room group
31
+ await self.channel_layer.group_add(
32
+ self.room_group_name,
33
+ self.channel_name
34
+ )
35
+
36
+ await self.accept()
37
+ else:
38
+ await self.close()
39
+
40
+ async def disconnect(self, close_code):
41
+ # Leave room group
42
+ await self.channel_layer.group_discard(
43
+ self.room_group_name,
44
+ self.channel_name
45
+ )
46
+
47
+ async def receive(self, text_data):
48
+ user = self.scope["user"]
49
+ if not user.is_authenticated:
50
+ await self.close()
51
+ return
52
+
53
+ text_data_json = json.loads(text_data)
54
+ message = text_data_json['message']
55
+ sender_id = self.scope['user'].id
56
+ # sender_id = text_data_json['sender_id']
57
+ sender = await sync_to_async(User.objects.get)(id=sender_id)
58
+
59
+ # Save message to database
60
+ chat_message = await sync_to_async(MessageChat.objects.create)(
61
+ chat_room=self.chat_room,
62
+ sender=sender,
63
+ text=message
64
+ )
65
+
66
+ # Send message to room group
67
+ await self.channel_layer.group_send(
68
+ self.room_group_name,
69
+ {
70
+ 'type': 'chat_message',
71
+ 'message': chat_message.text,
72
+ 'sender': sender.username
73
+ }
74
+ )
75
+
76
+ async def chat_message(self, event):
77
+ message = event['message']
78
+ sender = event['sender']
79
+
80
+ # Send message to WebSocket
81
+ await self.send(text_data=json.dumps({
82
+ 'message': message,
83
+ 'sender': sender
84
+ }))
@@ -2,6 +2,13 @@ from django.utils.timezone import datetime
2
2
  from .models import Session
3
3
  import user_agents
4
4
 
5
+ from urllib.parse import parse_qs
6
+ from channels.middleware import BaseMiddleware
7
+ from django.contrib.auth.models import AnonymousUser
8
+ from rest_framework_simplejwt.tokens import AccessToken
9
+ from django.contrib.auth import get_user_model
10
+ from asgiref.sync import sync_to_async
11
+
5
12
 
6
13
  class TakeUserSessionMiddlaware:
7
14
  def __init__(self, get_response):
@@ -41,4 +48,33 @@ class TakeUserSessionMiddlaware:
41
48
  x_forwarded_for = request.META.get('X_FORWARDED_FOR')
42
49
  if x_forwarded_for:
43
50
  return x_forwarded_for.split(',')[0]
44
- return request.META.get('REMOTE_ADDR')
51
+ return request.META.get('REMOTE_ADDR')
52
+
53
+
54
+
55
+ User = get_user_model()
56
+
57
+ @sync_to_async
58
+ def get_user(validated_token):
59
+ try:
60
+ user_id = validated_token["user_id"]
61
+ return User.objects.get(id=user_id)
62
+ except User.DoesNotExist:
63
+ return AnonymousUser()
64
+
65
+ class JWTAuthMiddleware(BaseMiddleware):
66
+ async def __call__(self, scope, receive, send):
67
+ query_string = scope.get("query_string", b"").decode()
68
+ query_params = parse_qs(query_string)
69
+ token = query_params.get("token")
70
+
71
+ if token:
72
+ try:
73
+ access_token = AccessToken(token[0])
74
+ scope["user"] = await get_user(access_token)
75
+ except Exception as e:
76
+ scope["user"] = AnonymousUser()
77
+ else:
78
+ scope["user"] = AnonymousUser()
79
+
80
+ return await super().__call__(scope, receive, send)
@@ -0,0 +1,17 @@
1
+ # Generated by Django 5.1.6 on 2025-08-14 19:00
2
+
3
+ from django.db import migrations
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('drfchelseru', '0003_rename_mobile_otpcode_mobile_number'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.RenameModel(
14
+ old_name='Message',
15
+ new_name='Message_SMS',
16
+ ),
17
+ ]
@@ -0,0 +1,37 @@
1
+ # Generated by Django 5.1.6 on 2025-08-14 19:01
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('drfchelseru', '0004_rename_message_message_sms'),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.RenameModel(
15
+ old_name='Message_SMS',
16
+ new_name='MessageSMS',
17
+ ),
18
+ migrations.CreateModel(
19
+ name='ChatRoom',
20
+ fields=[
21
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
22
+ ('created_at', models.DateTimeField(auto_now_add=True)),
23
+ ('user_1', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user1_chats', to='drfchelseru.user')),
24
+ ('user_2', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user2_chats', to='drfchelseru.user')),
25
+ ],
26
+ ),
27
+ migrations.CreateModel(
28
+ name='MessageChat',
29
+ fields=[
30
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
31
+ ('text', models.TextField()),
32
+ ('timestamp', models.DateTimeField(auto_now_add=True)),
33
+ ('chat_room', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='messages', to='drfchelseru.chatroom')),
34
+ ('sender', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='drfchelseru.user')),
35
+ ],
36
+ ),
37
+ ]
@@ -0,0 +1,36 @@
1
+ # Generated by Django 5.1.6 on 2025-08-14 19:36
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', '0005_rename_message_sms_messagesms_chatroom_messagechat'),
12
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
13
+ ]
14
+
15
+ operations = [
16
+ migrations.AlterField(
17
+ model_name='chatroom',
18
+ name='user_1',
19
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user1_chats_drf_chelseru', to=settings.AUTH_USER_MODEL),
20
+ ),
21
+ migrations.AlterField(
22
+ model_name='chatroom',
23
+ name='user_2',
24
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user2_chats_drf_chelseru', to=settings.AUTH_USER_MODEL),
25
+ ),
26
+ migrations.AlterField(
27
+ model_name='messagechat',
28
+ name='chat_room',
29
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='messages_chat_drf_chelseru', to='drfchelseru.chatroom'),
30
+ ),
31
+ migrations.AlterField(
32
+ model_name='messagechat',
33
+ name='sender',
34
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
35
+ ),
36
+ ]
@@ -0,0 +1,131 @@
1
+ # Generated by Django 5.1.6 on 2025-08-16 09:02
2
+
3
+ import django.db.models.deletion
4
+ import django.utils.timezone
5
+ from django.conf import settings
6
+ from django.db import migrations, models
7
+
8
+
9
+ class Migration(migrations.Migration):
10
+
11
+ dependencies = [
12
+ ("drfchelseru", "0006_alter_chatroom_user_1_alter_chatroom_user_2_and_more"),
13
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
14
+ ]
15
+
16
+ operations = [
17
+ migrations.AddField(
18
+ model_name="chatroom",
19
+ name="pinned_for",
20
+ field=models.ManyToManyField(
21
+ blank=True,
22
+ related_name="pinned_drf_chelseru",
23
+ to=settings.AUTH_USER_MODEL,
24
+ ),
25
+ ),
26
+ migrations.AddField(
27
+ model_name="chatroom",
28
+ name="status",
29
+ field=models.IntegerField(
30
+ default=1, verbose_name=[(1, "Open"), (2, "Close")]
31
+ ),
32
+ ),
33
+ migrations.AddField(
34
+ model_name="chatroom",
35
+ name="users",
36
+ field=models.ManyToManyField(
37
+ blank=True,
38
+ related_name="users_drf_chelseru",
39
+ to=settings.AUTH_USER_MODEL,
40
+ ),
41
+ ),
42
+ migrations.AddField(
43
+ model_name="messagechat",
44
+ name="created_at",
45
+ field=models.DateTimeField(
46
+ auto_now_add=True, default=django.utils.timezone.now
47
+ ),
48
+ preserve_default=False,
49
+ ),
50
+ migrations.CreateModel(
51
+ name="ChatRoomPermissions",
52
+ fields=[
53
+ (
54
+ "id",
55
+ models.BigAutoField(
56
+ auto_created=True,
57
+ primary_key=True,
58
+ serialize=False,
59
+ verbose_name="ID",
60
+ ),
61
+ ),
62
+ (
63
+ "access",
64
+ models.IntegerField(
65
+ choices=[
66
+ (0, "Read Only"),
67
+ (1, "Member"),
68
+ (2, "Can Add Members"),
69
+ (3, "Can Kick Members"),
70
+ (4, "Can Ban Members"),
71
+ (5, "Can Add Access"),
72
+ (6, "Can Remove Accessa"),
73
+ (7, "Can Kick Admins"),
74
+ (8, "Can Close Chat Room"),
75
+ (9, "Can ReOpen Chat Room"),
76
+ (10, "Can Rename Chat Room"),
77
+ (11, "Can Update Chatroom Avatar"),
78
+ (12, "Can Delete Chat Room"),
79
+ ],
80
+ default=1,
81
+ ),
82
+ ),
83
+ ("user", models.ManyToManyField(to=settings.AUTH_USER_MODEL)),
84
+ ],
85
+ ),
86
+ migrations.AddField(
87
+ model_name="chatroom",
88
+ name="permissions",
89
+ field=models.ManyToManyField(
90
+ blank=True,
91
+ related_name="permissions_drf_chelseru",
92
+ to="drfchelseru.chatroompermissions",
93
+ ),
94
+ ),
95
+ migrations.CreateModel(
96
+ name="Organization",
97
+ fields=[
98
+ (
99
+ "id",
100
+ models.BigAutoField(
101
+ auto_created=True,
102
+ primary_key=True,
103
+ serialize=False,
104
+ verbose_name="ID",
105
+ ),
106
+ ),
107
+ ("name", models.CharField(max_length=45)),
108
+ ("uname", models.CharField(max_length=45, unique=True)),
109
+ ("created_at", models.DateTimeField(auto_now_add=True)),
110
+ (
111
+ "owner",
112
+ models.OneToOneField(
113
+ on_delete=django.db.models.deletion.DO_NOTHING,
114
+ related_name="organization_drf_chelseru",
115
+ to=settings.AUTH_USER_MODEL,
116
+ ),
117
+ ),
118
+ ],
119
+ ),
120
+ migrations.AddField(
121
+ model_name="chatroom",
122
+ name="organization",
123
+ field=models.ForeignKey(
124
+ blank=True,
125
+ null=True,
126
+ on_delete=django.db.models.deletion.DO_NOTHING,
127
+ related_name="chatroom_drf_chelseru",
128
+ to="drfchelseru.organization",
129
+ ),
130
+ ),
131
+ ]
@@ -0,0 +1,25 @@
1
+ # Generated by Django 5.1.6 on 2025-08-16 09:02
2
+
3
+ from django.conf import settings
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ (
11
+ "drfchelseru",
12
+ "0007_chatroom_pinned_for_chatroom_status_chatroom_users_and_more",
13
+ ),
14
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
15
+ ]
16
+
17
+ operations = [
18
+ migrations.AlterField(
19
+ model_name="chatroompermissions",
20
+ name="user",
21
+ field=models.ManyToManyField(
22
+ related_name="user_drf_chelseru", to=settings.AUTH_USER_MODEL
23
+ ),
24
+ ),
25
+ ]
@@ -0,0 +1,20 @@
1
+ # Generated by Django 5.1.6 on 2025-08-16 09:26
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ("drfchelseru", "0008_alter_chatroompermissions_user"),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.AlterField(
14
+ model_name="chatroom",
15
+ name="status",
16
+ field=models.IntegerField(
17
+ default=1, verbose_name=[(1, "Open"), (2, "Close"), (3, "Hold")]
18
+ ),
19
+ ),
20
+ ]
@@ -0,0 +1,41 @@
1
+ # Generated by Django 5.1.6 on 2025-08-16 09:30
2
+
3
+ from django.conf import settings
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ("drfchelseru", "0009_alter_chatroom_status"),
11
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12
+ ]
13
+
14
+ operations = [
15
+ migrations.AddField(
16
+ model_name="chatroom",
17
+ name="banneds",
18
+ field=models.ManyToManyField(
19
+ blank=True,
20
+ related_name="bans_drf_chelseru",
21
+ to=settings.AUTH_USER_MODEL,
22
+ ),
23
+ ),
24
+ migrations.AddField(
25
+ model_name="chatroom",
26
+ name="descriptions",
27
+ field=models.TextField(blank=True, null=True),
28
+ ),
29
+ migrations.AddField(
30
+ model_name="chatroom",
31
+ name="name",
32
+ field=models.CharField(blank=True, max_length=45, null=True),
33
+ ),
34
+ migrations.AlterField(
35
+ model_name="chatroom",
36
+ name="status",
37
+ field=models.IntegerField(
38
+ choices=[(1, "Open"), (2, "Close"), (3, "Hold")], default=1
39
+ ),
40
+ ),
41
+ ]
drfchelseru/models.py CHANGED
@@ -1,9 +1,11 @@
1
1
  from django.db import models
2
+ from django.contrib.auth import get_user_model
2
3
  from django.contrib.auth.models import User as default_user
3
4
  from django.utils.timezone import now, timedelta
4
5
  from random import randint
5
6
  from .settings import auth_init_check
6
7
 
8
+ UserGet = get_user_model()
7
9
 
8
10
  class User(models.Model):
9
11
  user = models.OneToOneField(default_user, on_delete=models.CASCADE, related_name='mobile_drf_chelseru')
@@ -64,7 +66,7 @@ class Session(models.Model):
64
66
  return f'{self.user} - {self.ip_address}'
65
67
 
66
68
 
67
- class Message(models.Model):
69
+ class MessageSMS(models.Model):
68
70
  message_text = models.TextField()
69
71
  mobile_number = models.CharField(max_length=20)
70
72
  _from = models.CharField(max_length=20, blank=True, null=True)
@@ -74,4 +76,92 @@ class Message(models.Model):
74
76
  created_at = models.DateTimeField(auto_now_add=True)
75
77
 
76
78
  def __str__(self):
77
- return f'to: {self.mobile_number} , at: {self.created_at}'
79
+ return f'to: {self.mobile_number} , at: {self.created_at}'
80
+
81
+
82
+ class Organization(models.Model):
83
+ owner = models.OneToOneField(UserGet, related_name='organization_drf_chelseru', on_delete=models.DO_NOTHING)
84
+ name = models.CharField(max_length=45)
85
+ uname = models.CharField(max_length=45, unique=True)
86
+ created_at = models.DateTimeField(auto_now_add=True)
87
+
88
+ def __str__(self):
89
+ return f'owner: {self.owner} - unique name: {self.uname}'
90
+
91
+
92
+ class ChatRoomPermissions(models.Model):
93
+ READ_ONLY = 0
94
+ MEMBER = 1
95
+ CAN_ADD_MEMBER = 2
96
+ CAN_KICK_MEMBER = 3
97
+ CAN_BAN_MEMBER = 4
98
+ CAN_ADD_ACCESS = 5
99
+ CAN_REMOVE_ACCESS = 6
100
+ CAN_KICK_ADMIN = 7
101
+ CAN_CLOSE_CHAT = 8
102
+ CAN_REOPEN_CHAT = 9
103
+ CAN_RENAME_CHAT = 10
104
+ CAN_UPDATE_AVATAR = 11
105
+ CAN_DELETE_CHAT = 12
106
+
107
+ ACCESS_CHOICES = [
108
+ (READ_ONLY, 'Read Only'),
109
+ (MEMBER, 'Member'),
110
+ (CAN_ADD_MEMBER, 'Can Add Members'),
111
+ (CAN_KICK_MEMBER, 'Can Kick Members'),
112
+ (CAN_BAN_MEMBER, 'Can Ban Members'),
113
+ (CAN_ADD_ACCESS, 'Can Add Access'),
114
+ (CAN_REMOVE_ACCESS, 'Can Remove Accessa'),
115
+ (CAN_KICK_ADMIN, 'Can Kick Admins'),
116
+ (CAN_CLOSE_CHAT, 'Can Close Chat Room'),
117
+ (CAN_REOPEN_CHAT, 'Can ReOpen Chat Room'),
118
+ (CAN_RENAME_CHAT, 'Can Rename Chat Room'),
119
+ (CAN_UPDATE_AVATAR, 'Can Update Chatroom Avatar'),
120
+ (CAN_DELETE_CHAT, 'Can Delete Chat Room'),
121
+ ]
122
+ user = models.ManyToManyField(UserGet, related_name='user_drf_chelseru')
123
+ access = models.IntegerField(choices=ACCESS_CHOICES, default=MEMBER)
124
+
125
+ def __str__(self):
126
+ return f'user: {self.user} - access: {self.access}'
127
+
128
+
129
+ class ChatRoom(models.Model):
130
+ STATUS_OPEN = 1
131
+ STATUS_CLOSE = 2
132
+ STATUS_HOLD = 3
133
+
134
+ STATUS_CHOICES = [
135
+ (STATUS_OPEN, 'Open'),
136
+ (STATUS_CLOSE, 'Close'),
137
+ (STATUS_HOLD, 'Hold')
138
+ ]
139
+
140
+ user_1 = models.ForeignKey(UserGet, on_delete=models.CASCADE, related_name='user1_chats_drf_chelseru')
141
+ user_2 = models.ForeignKey(UserGet, on_delete=models.CASCADE, related_name='user2_chats_drf_chelseru')
142
+
143
+ users = models.ManyToManyField(UserGet, blank=True, related_name='users_drf_chelseru')
144
+ organization = models.ForeignKey(Organization, on_delete=models.DO_NOTHING, related_name='chatroom_drf_chelseru', blank=True, null=True)
145
+ pinned_for = models.ManyToManyField(UserGet, blank=True, related_name='pinned_drf_chelseru')
146
+ permissions = models.ManyToManyField(ChatRoomPermissions, blank=True, related_name='permissions_drf_chelseru')
147
+ status = models.IntegerField(choices=STATUS_CHOICES, default=STATUS_OPEN)
148
+ banneds = models.ManyToManyField(UserGet, blank=True, related_name='bans_drf_chelseru')
149
+ name = models.CharField(max_length=45, blank=True, null=True)
150
+ descriptions = models.TextField(blank=True, null=True)
151
+
152
+ created_at = models.DateTimeField(auto_now_add=True)
153
+
154
+ def __str__(self):
155
+ return f"ID: {self.id} | Chat between {self.user_1.username} and {self.user_2.username}"
156
+
157
+
158
+ class MessageChat(models.Model):
159
+ chat_room = models.ForeignKey(ChatRoom, on_delete=models.CASCADE, related_name='messages_chat_drf_chelseru')
160
+ sender = models.ForeignKey(UserGet, on_delete=models.CASCADE)
161
+ text = models.TextField()
162
+ timestamp = models.DateTimeField(auto_now_add=True)
163
+
164
+ created_at = models.DateTimeField(auto_now_add=True)
165
+
166
+ def __str__(self):
167
+ return f"iD: {self.id} | Message from {self.sender.username} at {self.timestamp} | Chatroom ID: {self.chat_room.id}"
drfchelseru/routing.py ADDED
@@ -0,0 +1,6 @@
1
+ from django.urls import re_path
2
+ from . import consumers
3
+
4
+ websocket_urlpatterns = [
5
+ re_path(r'ws/chat/(?P<chat_room_id>\d+)/$', consumers.ChatConsumer.as_asgi()),
6
+ ]
@@ -1,7 +1,10 @@
1
1
  from rest_framework.serializers import ModelSerializer
2
2
  from django.contrib.auth.models import User
3
- from .models import User as mobile, OTPCode, Session, Message
3
+ from .models import User as mobile, OTPCode, Session, MessageSMS, ChatRoom, MessageChat
4
4
 
5
+ # from django.contrib.auth import get_user_model
6
+
7
+ # UserGet = get_user_model()
5
8
 
6
9
  class DefaultUserSerializer(ModelSerializer):
7
10
  class Meta:
@@ -30,5 +33,22 @@ class SessionSerializer(ModelSerializer):
30
33
 
31
34
  class MessageSerializer(ModelSerializer):
32
35
  class Meta:
33
- model = Message
34
- fields = '__all__'
36
+ model = MessageSMS
37
+ fields = '__all__'
38
+
39
+
40
+ class ChatRoomSerializer(ModelSerializer):
41
+ user_1, user_2 = DefaultUserSerializer(read_only=True), DefaultUserSerializer(read_only=True)
42
+ class Meta:
43
+ model = ChatRoom
44
+ fields = '__all__'
45
+ depth = 1
46
+
47
+
48
+ class MessageChatSerializer(ModelSerializer):
49
+ sender = DefaultUserSerializer(read_only=True)
50
+
51
+ class Meta:
52
+ model = MessageChat
53
+ fields = '__all__'
54
+ depth = 1
drfchelseru/services.py CHANGED
@@ -152,7 +152,7 @@ class KavenegarCom:
152
152
 
153
153
 
154
154
 
155
- def send_message(mobile_number, message_text, data, template_id=None):
155
+ def send_message(mobile_number, message_text, data=None, template_id=None):
156
156
  try:
157
157
  icheck = sms_init_check()
158
158
  if not (icheck and isinstance(icheck, dict) and 'SMS_SERVICE' in icheck and 'SETTINGS' in icheck):
drfchelseru/settings.py CHANGED
@@ -6,7 +6,7 @@ DJANGO_CHELSERU = {
6
6
  'OPTIONS': {
7
7
  'OTP_LENGTH' : 8, # DEFAULT 8
8
8
  'OTP_EXPIRE_PER_MINUTES': 4, # DEFAULT 4
9
- 'OTP_SMS_TEMPLATE_ID' : 1,
9
+ 'OTP_SMS_TEMPLATE_ID' : 1,
10
10
  }
11
11
  },
12
12
  'SMS': {
drfchelseru/signals.py CHANGED
@@ -4,6 +4,16 @@ from django.db.models.signals import post_save, pre_save
4
4
  from django.dispatch import receiver
5
5
  import requests
6
6
 
7
+ from urllib.parse import parse_qs
8
+ from channels.middleware import BaseMiddleware
9
+ from django.contrib.auth.models import AnonymousUser
10
+ from rest_framework_simplejwt.tokens import AccessToken
11
+ from django.contrib.auth import get_user_model
12
+ from asgiref.sync import sync_to_async
13
+
14
+ UserGet = get_user_model()
15
+
16
+
7
17
  # @receiver(post_save, sender=User)
8
18
  # def create_user_profile(sender, instance, created, **kwargs):
9
19
  # if created:
@@ -35,4 +45,32 @@ def send_email_after_create(sender, instance, **kwargs):
35
45
  except:
36
46
  pass
37
47
 
38
-
48
+
49
+
50
+
51
+
52
+
53
+ @sync_to_async
54
+ def get_user(validated_token):
55
+ try:
56
+ user_id = validated_token["user_id"]
57
+ return UserGet.objects.get(id=user_id)
58
+ except UserGet.DoesNotExist:
59
+ return AnonymousUser()
60
+
61
+ class JWTAuthMiddleware(BaseMiddleware):
62
+ async def __call__(self, scope, receive, send):
63
+ query_string = scope.get("query_string", b"").decode()
64
+ query_params = parse_qs(query_string)
65
+ token = query_params.get("token")
66
+
67
+ if token:
68
+ try:
69
+ access_token = AccessToken(token[0])
70
+ scope["user"] = await get_user(access_token)
71
+ except Exception as e:
72
+ scope["user"] = AnonymousUser()
73
+ else:
74
+ scope["user"] = AnonymousUser()
75
+
76
+ return await super().__call__(scope, receive, send)
drfchelseru/urls.py CHANGED
@@ -1,11 +1,16 @@
1
- from django.urls import path
2
- from .views import MessageSend, OTPCodeSend ,Authentication, SessionList
1
+ from django.urls import path, include
2
+ from rest_framework.routers import DefaultRouter
3
+ from .views import MessageSend, OTPCodeSend ,Authentication, SessionList, MessageViewSet, ChatRoomViewSet
3
4
 
4
5
  app_name = 'drfchelseru'
5
6
 
7
+ router = DefaultRouter()
8
+ router.register(r'chatrooms', ChatRoomViewSet, basename='chatroom')
9
+ router.register(r'messages', MessageViewSet, basename='messages')
10
+
6
11
  urlpatterns = [
7
12
  path('message/send/', MessageSend.as_view(), name='message-send'),
8
13
  path('otp/send/', OTPCodeSend.as_view(), name='otp-send'),
9
14
  path('authenticate/', Authentication.as_view(), name='auth'),
10
15
  path('sessions/', SessionList.as_view(), name='sessions'),
11
- ]
16
+ ] + [path('chat/', include(router.urls)),]
drfchelseru/views.py CHANGED
@@ -1,13 +1,17 @@
1
+ from rest_framework.viewsets import ModelViewSet
1
2
  from rest_framework.views import APIView
2
3
  from rest_framework.permissions import AllowAny, IsAuthenticated
3
4
  from rest_framework.generics import ListAPIView
4
5
  from rest_framework.response import Response
5
6
  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, HTTP_409_CONFLICT
7
+ from rest_framework.exceptions import NotFound, ValidationError
8
+ from django.contrib.auth.models import User as UserDefault
9
+
6
10
  from .services import send_message
7
11
  from .settings import sms_init_check, auth_init_check
8
12
  from .validators import mobile_number as mobile_validator
9
- from .serializers import MessageSerializer, OTPCodeSerializer, SessionSerializer
10
- from .models import User
13
+ from .serializers import MessageSerializer, OTPCodeSerializer, SessionSerializer, ChatRoomSerializer, MessageChatSerializer
14
+ from .models import User, ChatRoom
11
15
  from django.utils.timezone import now, timedelta
12
16
  from django.db import transaction
13
17
 
@@ -210,4 +214,52 @@ class Authentication(APIView):
210
214
  class SessionList(ListAPIView):
211
215
  permission_classes = (IsAuthenticated, )
212
216
  serializer_class = SessionSerializer
213
- queryset = serializer_class.Meta.model.objects.all()
217
+ queryset = serializer_class.Meta.model.objects.all()
218
+
219
+
220
+ class ChatRoomViewSet(ModelViewSet):
221
+ serializer_class = ChatRoomSerializer
222
+ permission_classes = [IsAuthenticated]
223
+ model = serializer_class.Meta.model
224
+
225
+ def get_queryset(self):
226
+ return self.model.objects.filter(user_1=self.request.user) | self.model.objects.filter(user_2=self.request.user)
227
+
228
+ def perform_create(self, serializer):
229
+ user = self.request.user
230
+
231
+ user_id = self.request.data.get('user', None)
232
+ user_2 = UserDefault.objects.filter(id=user_id).first()
233
+ if not user_2:
234
+ raise NotFound("کاربر مورد نظر با آی دی فرستاده شده یافت نشد.")
235
+
236
+ chat_room = serializer.save(user_1=user, user_2=user_2)
237
+
238
+
239
+ class MessageViewSet(ModelViewSet):
240
+ serializer_class = MessageChatSerializer
241
+ permission_classes = [IsAuthenticated]
242
+
243
+ def get_queryset(self):
244
+ user = self.request.user
245
+
246
+ queryset = self.serializer_class.Meta.model.objects.all()
247
+ chat_room_id = self.request.query_params.get('chat_room')
248
+ if chat_room_id:
249
+ queryset = queryset.filter(chat_room_id=chat_room_id)
250
+ return queryset
251
+
252
+ def perform_create(self, serializer):
253
+ chat_room_id = self.request.data.get('chat_room')
254
+ if not chat_room_id:
255
+ raise ValidationError("فیلد chat_room اجباریه.")
256
+
257
+ try:
258
+ chat = ChatRoom.objects.get(id=chat_room_id)
259
+ except ChatRoom.DoesNotExist:
260
+ raise NotFound("چت‌روم پیدا نشد.")
261
+
262
+ message = serializer.save(sender=self.request.user, chat_room=chat)
263
+ chat = message.chat_room
264
+ chat.save()
265
+
@@ -1,95 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: django-chelseru
3
- Version: 1.0.2
4
- Summary: Authentication system, online and real-time chat, SMS system for Iranian SMS services.
5
- Home-page: https://pipdjango.chelseru.com
6
- Author: Sobhan Bahman|Rashnu
7
- Author-email: bahmanrashnu@gmail.com
8
- Project-URL: Documentation, https://github.com/Chelseru/django-chelseru-lour/
9
- Project-URL: Telegram Group, https://t.me/bahmanpy
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 djangoauth auth ywt otpauth otp authentication djangootp djangoiransms iransms djangosms djangokavenegar djangomelipayamak sobhan چت سبحان بهمن رشنو چلسرو جنگو پایتون لر لور آنلاین ریل تایم
12
- Classifier: Programming Language :: Python :: 3
13
- Classifier: Framework :: Django
14
- Classifier: License :: OSI Approved :: MIT License
15
- Classifier: Operating System :: OS Independent
16
- Requires-Python: >=3.11
17
- Description-Content-Type: text/markdown
18
- License-File: LICENSE
19
- Requires-Dist: Django>=5.1.6
20
- Requires-Dist: djangorestframework==3.15.2
21
- Requires-Dist: djangorestframework_simplejwt==5.5.0
22
- Requires-Dist: channels==4.2.2
23
- Requires-Dist: channels_redis==4.2.1
24
- Requires-Dist: daphne==4.1.2
25
- Requires-Dist: zeep==4.3.1
26
- Requires-Dist: user-agents==2.2.0
27
- Dynamic: author
28
- Dynamic: author-email
29
- Dynamic: classifier
30
- Dynamic: description
31
- Dynamic: description-content-type
32
- Dynamic: home-page
33
- Dynamic: keywords
34
- Dynamic: license-file
35
- Dynamic: project-url
36
- Dynamic: requires-dist
37
- Dynamic: requires-python
38
- Dynamic: summary
39
-
40
- django-chelseruیک بسته جنگویی برای گپ‌زنی همزمان، راستی‌آزمایی پیامکی و فرستادن پیامک با یاری‌دهنده‌های ایرانی.نویسندهSobhan Bahman Rashnu🚀 ویژگی‌ها📱 راستی‌آزمایی پیامکی (رمز یک‌بارمصرف): راستی‌آزمایی امن کاربران با یاری‌گیری از رمزهای یک‌بارمصرف که از راه پیامک فرستاده می‌شوند.💬 گپ‌زنی همزمان: کارکرد پیام‌رسانی همزمان بر پایه WebSocket.✉️ سامانه‌های پیامکی: فرستادن پیامک از راه یاری‌دهنده‌های نامور پیامکی ایرانی.⚙️ نصببسته را با یاری‌گیری از pip نصب کنید:pip install django-chelseru
41
- 'drfchelseru' را به INSTALLED_APPS در پرونده settings.py خود بیفزایید:INSTALLED_APPS = [
42
- ...
43
- 'channels',
44
- 'rest_framework',
45
- 'rest_framework_simplejwt',
46
- 'drfchelseru',
47
- ...
48
- ]
49
- 🛠️ پیکربندیبرای پیکربندی بسته، واژه‌نامه DJANGO_CHELSERU را به پرونده settings.py خود بیفزایید. این واژه‌نامه به شما پروانه می‌دهد تا چیدمان‌های راستی‌آزمایی و پیامک را خودساخته نمایید.# settings.py
50
-
51
- DJANGO_CHELSERU = {
52
- 'AUTH': {
53
- 'AUTH_METHOD' : 'OTP', # روش‌های پشتیبانی شده: OTP, PASSWD
54
- 'AUTH_SERVICE' : 'rest_framework_simplejwt', # سرویس‌های پشتیبانی شده: rest_framework_simplejwt
55
- 'OPTIONS': {
56
- 'OTP_LENGTH' : 8, # پیش‌فرض: 8
57
- 'OTP_EXPIRE_PER_MINUTES': 4, # پیش‌فرض: 4
58
- 'OTP_SMS_TEMPLATE_ID' : 1, # شناسه قالب پیامکی برای رمز یک‌بارمصرف
59
- }
60
- },
61
- 'SMS': {
62
- 'SMS_SERVICE': 'PARSIAN_WEBCO_IR', # یاری‌دهنده‌های پشتیبانی شده: PARSIAN_WEBCO_IR, MELI_PAYAMAK_COM, KAVENEGAR_COM
63
- 'SETTINGS': {
64
- 'PARSIAN_WEBCO_IR_API_KEY' : 'YOUR_PARSIAN_WEBCO_API_KEY',
65
- 'MELI_PAYAMAK_COM_USERNAME' : 'YOUR_MELI_PAYAMAK_USERNAME',
66
- 'MELI_PAYAMAK_COM_PASSWORD' : 'YOUR_MELI_PAYAMAK_PASSWORD',
67
- 'MELI_PAYAMAK_COM_FROM' : 'YOUR_MELI_PAYAMAK_FROM_NUMBER',
68
- 'KAVENEGAR_COM_API_KEY' : 'YOUR_KAVENEGAR_API_KEY',
69
- 'KAVENEGAR_COM_FROM' : 'YOUR_KAVENEGAR_FROM_NUMBER',
70
- },
71
- 'TEMPLATES': {
72
- 'T1': 1,
73
- 'T2': 2,
74
- ...
75
- }
76
- }
77
- }
78
- AUTH_METHOD: روش راستی‌آزمایی را روشن می‌سازد. برای راستی‌آزمایی پیامکی، از 'OTP' بهره بگیرید.OTP_LENGTH: درازای رمز یک‌بارمصرف.OTP_EXPIRE_PER_MINUTES: زمان پایان‌یافتن رمز یک‌بارمصرف بر پایه دقیقه.OTP_SMS_TEMPLATE_ID: شناسه قالب پیامکی که برای فرستادن رمز یک‌بارمصرف بهره گرفته می‌شود.SMS_SERVICE: یاری‌دهنده پیامکی دلخواه خود را برگزینید.SETTINGS: آگاهی‌های نیاز برای یاری‌دهنده پیامکی برگزیده‌تان را فراهم آورید.TEMPLATES: شناسه‌های قالب پیامکی خود را روشن سازید.🔌 نقطه‌های پایانیبرای به کارگیری کارکردهای این بسته، URLهای زیر را به پرونده urls.py خود بیفزایید.# urls.py
79
-
80
- from django.urls import path, include
81
-
82
- urlpatterns = [
83
- ...
84
- path('api/', include('drfchelseru.urls')),
85
- ...
86
- ]
87
- این بسته نقطه‌های پایانی API زیر را فراهم می‌آورد:نقطه‌ پایانیشرحروش/api/otp/send/یک رمز یک‌بارمصرف به شماره همراه گفته‌شده می‌فرستد.POST/api/authenticate/کاربری را با رمز یک‌بارمصرف دریافت‌شده، راستی‌آزمایی می‌کند.POST/api/sessions/نشست‌های فعال کاربر را فهرست کرده و درایوری می‌کند.GET/api/message/send/یک پیامک با یاری‌دهنده پیکربندی‌شده می‌فرستد.POSTبه کارگیری نقطه‌های پایانی1. فرستادن رمز یک‌بارمصرف (/api/otp/send/)روش: POSTشرح: یک رمز یک‌بارمصرف به شماره همراه کاربر می‌فرستد.داده‌نماهای نیاز:داده‌نماگونهشرحنمونهmobile_numberstrشماره همراه کاربر.09121234567پاسخ‌ها:HTTP 200 OK: رمز یک‌بارمصرف با کامیابی فرستاده شد.{"details": "The OTP code was sent correctly."}
88
- HTTP 400 BAD REQUEST: ساختار نادرست mobile_number.HTTP 409 CONFLICT: یک رمز یک‌بارمصرف پیش‌تر فرستاده شده و هنوز روایی دارد.{"details": "An OTP code has already been sent. Please wait X seconds before trying again."}
89
- HTTP 500 INTERNAL SERVER ERROR: یک دشواری در کارگذار پیش آمده است.2. راستی‌آزمایی (/api/authenticate/)روش: POSTشرح: کاربر را با رمز یک‌بارمصرف فراهم شده، راستی‌آزمایی می‌کند. اگر با کامیابی انجام شود، توکن‌های JWT (access و refresh) را بازمی‌گرداند.داده‌نماهای نیاز:داده‌نماگونهشرحنمونهmobile_numberstrشماره همراه کاربر.09121234567codestrرمز یک‌بارمصرف دریافت شده از راه پیامک.12345678groupintاختیاری: یک شناسه دسته برای کاربر.1پاسخ‌ها:HTTP 200 OK: راستی‌آزمایی با کامیابی انجام شد.{
90
- "access": "...",
91
- "refresh": "..."
92
- }
93
- HTTP 401 UNAUTHORIZED: رمز یک‌بارمصرف ناروا یا پایان‌یافته.{"error": "The code sent to this mobile number was not found."}
94
- HTTP 400 BAD REQUEST: داده‌نماهای نیاز ناپیدا یا ساختار نادرست.HTTP 500 INTERNAL SERVER ERROR: یک دشواری در کارگذار پیش آمده است.3. فرستادن پیامک (/api/message/send/)روش: POSTشرح: یک پیامک خودساخته را با یاری‌دهنده پیکربندی‌شده می‌فرستد.داده‌نماهای نیاز:داده‌نماگونهشرحنمونهmobile_numberstrشماره همراه گیرنده.09121234567message_textstrنوشتار پیام. (بیشینه ۲۹۰ نویسه)Hello, World!template_idintبرای برخی یاری‌دهنده‌ها (برای نمونه پارسیان) نیاز است.1پاسخ‌ها:HTTP 200 OK: پیام با کامیابی فرستاده شد.{"details": "The Message was sent correctly."}
95
- HTTP 400 BAD REQUEST: دشواری‌های درست‌سنجی برای داده‌نماها.HTTP 401 UNAUTHORIZED: راستی‌آزمایی انجام نشد.HTTP 500 INTERNAL SERVER ERROR: یک دشواری در کارگذار پیش آمده است.HTTP 502 BAD GATEWAY: یاری‌دهنده پیامکی یک دشواری را بازگرداند.4. فهرست نشست‌ها (/api/sessions/)روش: GETشرح: همه نشست‌های فعال کاربران را فهرست می‌کند. نیاز به راستی‌آزمایی (IsAuthenticated) دارد.سربرگ‌های نیاز:سربرگارزشAuthorizationBearer <your_access_token>💡 مدل‌هااین بسته یک مدل Session برای درایوری نشست‌های فعال کاربران دارد. می‌توانید به این نشست‌ها از راه نقطه‌ پایانی /api/sessions/ دسترسی یافته و آنها را درایوری کنید.
@@ -1,22 +0,0 @@
1
- django_chelseru-1.0.2.dist-info/licenses/LICENSE,sha256=VupU5KV4NteHaNQb-WH31G_WZWezxXoomjiCIAHoQJo,1089
2
- drfchelseru/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- drfchelseru/admin.py,sha256=PmHIgD7XSObOTd9xx6_lgEfAr4Z_2bvkrnrHby1ZdK8,741
4
- drfchelseru/apps.py,sha256=hOTTzFGLXiTPZeN8p_LLcSECLtsR2Q0SUo8zJzgM-qQ,211
5
- drfchelseru/middlewares.py,sha256=ONzRCiiHrUicDXFVds2aTROb6QBIcWZyW_jXQgrAQAc,1450
6
- drfchelseru/models.py,sha256=ffTiDI7a1rVMjMFeeZqBqbkCsciwnKCowf2u8vj6VRs,2774
7
- drfchelseru/serializers.py,sha256=wYbvFmZuQzGEgi9S6B-kPuIAXJDcir6qKQuV6RgE3J4,769
8
- drfchelseru/services.py,sha256=V8mdeeHjmqUFAQLAyaEm3JTk-E8V8SfQMQhXoYdjt_c,10134
9
- drfchelseru/settings.py,sha256=pvGW6aMCrkW9kph_Zw6B_9qYeWb7wuIrUM1Lnu8kjhU,9055
10
- drfchelseru/signals.py,sha256=2jISUF_jQVZD06tOqHasXtneZKVCrpeIJQeHo9BjLc0,1428
11
- drfchelseru/tests.py,sha256=mrbGGRNg5jwbTJtWWa7zSKdDyeB4vmgZCRc2nk6VY-g,60
12
- drfchelseru/urls.py,sha256=SbSVU75NXNoH6YWvJrJbuo3kc6jgremOS8g0CLmk52w,410
13
- drfchelseru/validators.py,sha256=jAJASfG4kPcqrGxxIcoR8VjS4L4n_EzZuxJtQ1g9qmo,480
14
- drfchelseru/views.py,sha256=L_KcoHe_5SAJ-2xaRcYYL_V1Jgk1PwKJztV4wXpMa0s,10726
15
- drfchelseru/migrations/0001_initial.py,sha256=LfkWAeQuPXNW3_igM-w_pyKafCeHQF3aReYgMXfMzEE,1034
16
- drfchelseru/migrations/0002_otpcode_session_user.py,sha256=c3oyBTQo2kjsKr5r_XQrqaq-66F7pui-4H4mEyAXgJk,3126
17
- drfchelseru/migrations/0003_rename_mobile_otpcode_mobile_number.py,sha256=eiD7t6etg6nOeT7BhJrSfEcR1yTPePXtATgtyu30520,377
18
- drfchelseru/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- django_chelseru-1.0.2.dist-info/METADATA,sha256=iHcgLyl_b4q8kf4_VBN7YgHYxwnwG4OzADoCPqy7wWE,9553
20
- django_chelseru-1.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
21
- django_chelseru-1.0.2.dist-info/top_level.txt,sha256=fsaO1F03W3j4AYi0TfDGv5Cjb_Qrh6RSkwkWqfqaMns,12
22
- django_chelseru-1.0.2.dist-info/RECORD,,