├── chat_app ├── tests.py ├── __init__.py ├── migrations │ └── __init__.py ├── templatetags │ ├── __init__.py │ └── convert_date.py ├── apps.py ├── routing.py ├── urls.py ├── admin.py ├── signal.py ├── templates │ └── chat │ │ ├── home.html │ │ ├── create_friend.html │ │ ├── friend_list.html │ │ ├── base.html │ │ └── start_chat.html ├── models.py ├── views.py └── consumers.py ├── django_channel ├── __init__.py ├── urls.py ├── wsgi.py ├── asgi.py └── settings.py ├── chat_app video_ (1).mp4 ├── README.md └── manage.py /chat_app/tests.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /chat_app/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /django_channel/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /chat_app/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /chat_app/templatetags/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /chat_app video_ (1).mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pradip369/django-real-time-chat-application/HEAD/chat_app video_ (1).mp4 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # django-real-time-chat-application 2 | Real time chat application by using django channel 3 | 4 | 5 | 6 | see project working video : https://github.com/Pradip369/django-real-time-chat-application/raw/main/chat_app%20video_%20(1).mp4 7 | -------------------------------------------------------------------------------- /chat_app/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ChatAppConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'chat_app' 7 | 8 | def ready(self): 9 | from . import signal 10 | -------------------------------------------------------------------------------- /chat_app/templatetags/convert_date.py: -------------------------------------------------------------------------------- 1 | from django.template.library import Library 2 | import datetime 3 | register = Library() 4 | 5 | @register.filter(expects_localtime=True) 6 | def convert_date(value): 7 | return datetime.datetime.strptime(value, '%Y-%m-%d %H:%M:%S.%f') -------------------------------------------------------------------------------- /chat_app/routing.py: -------------------------------------------------------------------------------- 1 | 2 | from . import consumers 3 | from django.urls.conf import path 4 | 5 | websocket_urlpatterns = [ 6 | path('ws/chat//', consumers.ChatConsumer.as_asgi()), 7 | path('ws/personal_chat//', consumers.PersonalConsumer.as_asgi()), 8 | ] -------------------------------------------------------------------------------- /django_channel/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.urls import path 3 | from django.urls.conf import include 4 | 5 | urlpatterns = [ 6 | path('admin/', admin.site.urls), 7 | path('', include('chat_app.urls')), 8 | 9 | path('api_auth/', include('rest_framework.urls', namespace='rest_framework')) 10 | ] 11 | -------------------------------------------------------------------------------- /django_channel/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for django_channel project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_channel.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /chat_app/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from .views import * 3 | from django.views.generic.base import RedirectView 4 | 5 | urlpatterns = [ 6 | path('home/', home,name = 'home_page'), 7 | 8 | path('',RedirectView.as_view(url = '/home')), 9 | 10 | path('create_friend/', create_friend,name = 'create_friend'), 11 | 12 | path('friend_list/', friend_list,name = 'friend_list'), 13 | 14 | path('chat//', start_chat, name='start_chat'), 15 | ] 16 | -------------------------------------------------------------------------------- /django_channel/asgi.py: -------------------------------------------------------------------------------- 1 | import os 2 | from channels.auth import AuthMiddlewareStack 3 | from channels.routing import ProtocolTypeRouter, URLRouter 4 | from django.core.asgi import get_asgi_application 5 | import chat_app.routing 6 | from channels.security.websocket import AllowedHostsOriginValidator 7 | 8 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_channel.settings") 9 | 10 | application = ProtocolTypeRouter({ 11 | "http": get_asgi_application(), 12 | "websocket": AllowedHostsOriginValidator(AuthMiddlewareStack( 13 | URLRouter( 14 | chat_app.routing.websocket_urlpatterns 15 | ) 16 | )), 17 | }) -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_channel.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /chat_app/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import * 3 | from django_admin_inline_paginator.admin import TabularInlinePaginated 4 | 5 | class ChatMessageInline(TabularInlinePaginated): 6 | model = ChatMessage 7 | can_delete = False 8 | fields = ('user','message_detail') 9 | max_num = 0 10 | readonly_fields = fields 11 | per_page = 10 12 | 13 | @admin.register(ChatSession) 14 | class ChatSessionAdmin(admin.ModelAdmin): 15 | list_display= ["id","user1","user2",'updated_on'] 16 | search_fields=["id","user1__username","user2_username"] 17 | list_per_page = 10 18 | list_display_links = list_display 19 | inlines = [ChatMessageInline,] 20 | ordering = ['-updated_on'] 21 | 22 | @admin.register(Profile) 23 | class ProfileAdmin(admin.ModelAdmin): 24 | list_display= ["id","user","is_online"] 25 | search_fields=["id","user__username"] 26 | list_per_page = 10 27 | list_display_links = list_display 28 | ordering = ['is_online'] 29 | -------------------------------------------------------------------------------- /chat_app/signal.py: -------------------------------------------------------------------------------- 1 | from django.dispatch.dispatcher import receiver 2 | from django.db.models.signals import post_save 3 | from .models import ChatSession,ChatMessage,Profile 4 | from django.core.exceptions import ValidationError 5 | from django.contrib.auth.models import User 6 | 7 | @receiver(post_save,sender=ChatSession) 8 | def sender_receiver_no_same(sender,instance,created,**kwargs): 9 | if created: 10 | if instance.user1 == instance.user2: 11 | raise ValidationError("Sender and Receiver are not same!!",code='Invalid') 12 | 13 | @receiver(post_save,sender=User) 14 | def at_ending_save(sender,instance,created,**kwargs): 15 | if created: 16 | # UserChat.objects.create(user = instance) 17 | Profile.objects.create(user = instance) 18 | 19 | @receiver(post_save,sender=ChatMessage) 20 | def user_must_sender_or_receiver(sender,instance,created,**kwargs): 21 | if created: 22 | if instance.user != instance.chat_session.user1 and instance.user != instance.chat_session.user2: 23 | raise ValidationError("Invalid sender!!",code='Invalid') -------------------------------------------------------------------------------- /chat_app/templates/chat/home.html: -------------------------------------------------------------------------------- 1 | {% extends './base.html' %} 2 | 3 | {% block content %} 4 |
5 |

💬Chat App💬

6 |
7 |
8 |

9 | 10 | {% if request.user.is_authenticated %} 11 | Hello {{request.user.username}} : 12 | My Chat List 13 | {{unread_msg}} 14 | {% else %} 15 | Login Required : Login{% endif %} 16 | 17 | 18 |
19 |

🔀Add Friend in chat list🔀

20 |

21 | 22 | {% endblock %} 23 | 24 | {% block script %} 25 | 26 | 34 | 35 | {% endblock %} -------------------------------------------------------------------------------- /chat_app/templates/chat/create_friend.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | all_friends 8 | 9 | 10 |
11 |

Suggested Friend List

12 |
13 |
14 |
15 | 16 | {% if messages %} 17 | {% for message in messages%} 18 |

{{message}} 🔰See My Chat List🔰

19 | {% endfor %} 20 | {% endif %} 21 | 22 |
23 | {% for user in all_user %} 24 |
25 |

🧑 {{user.username| title}} 26 | 27 |

28 | {% endfor %} 29 |
30 |
31 |

No more users....

32 | 33 | -------------------------------------------------------------------------------- /chat_app/templates/chat/friend_list.html: -------------------------------------------------------------------------------- 1 | {% extends './base.html' %} 2 | 3 | {% block content %} 4 |

My ChatList

5 |
6 | 7 |
8 | {% for user in user_list %} 9 |
10 |
11 | 12 |

🧑 | {{user.user_name | title}}{{user.un_read_msg_count}}

13 |
14 | {% if user.status %}Online{% endif %} 15 |
16 | {% endfor %} 17 |
18 |
19 |

No more users....

20 | {% endblock %} 21 | 22 | {% block script %} 23 | 24 | 40 | 41 | {% endblock %} -------------------------------------------------------------------------------- /chat_app/templates/chat/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Home Page 8 | 9 | 10 | 11 | 12 | 13 | {% block content %} 14 | {% endblock %} 15 | 16 | 17 | 18 | 54 | 55 | {% block script %} 56 | {% endblock %} 57 | 58 | 59 | -------------------------------------------------------------------------------- /chat_app/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import User 3 | from django.db.models import Q 4 | import uuid 5 | 6 | class Profile(models.Model): 7 | user = models.OneToOneField(User,on_delete=models.CASCADE,related_name = 'profile_detail') 8 | ''' 9 | Add other fields... 10 | ''' 11 | is_online = models.BooleanField(default = False) 12 | 13 | class ChatSession(models.Model): 14 | user1 = models.ForeignKey(User,on_delete=models.CASCADE,related_name='user1_name') 15 | user2 = models.ForeignKey(User,on_delete=models.CASCADE,related_name='user2_name') 16 | updated_on = models.DateTimeField(auto_now = True) 17 | 18 | class Meta: 19 | unique_together = (("user1", "user2")) 20 | verbose_name = 'Chat Message' 21 | 22 | def __str__(self): 23 | return '%s_%s' %(self.user1.username,self.user2.username) 24 | 25 | @property 26 | def room_group_name(self): 27 | return f'chat_{self.id}' 28 | 29 | @staticmethod 30 | def chat_session_exists(user1,user2): 31 | return ChatSession.objects.filter(Q(user1=user1, user2=user2) | Q(user1=user2, user2=user1)).first() 32 | 33 | @staticmethod 34 | def create_if_not_exists(user1,user2): 35 | res = ChatSession.chat_session_exists(user1,user2) 36 | return False if res else ChatSession.objects.create(user1=user1,user2=user2) 37 | 38 | class ChatMessage(models.Model): 39 | id = models.UUIDField(primary_key = True,editable = False) 40 | chat_session = models.ForeignKey(ChatSession,on_delete=models.CASCADE,related_name='user_messages') 41 | user = models.ForeignKey(User, verbose_name='message_sender',on_delete=models.CASCADE) 42 | message_detail = models.JSONField() 43 | 44 | class Meta: 45 | ordering = ['-message_detail__timestamp'] 46 | 47 | def __str__(self): 48 | return '%s' %(self.message_detail["timestamp"]) 49 | 50 | def save(self,*args,**kwargs): 51 | super().save(*args,**kwargs) 52 | ChatSession.objects.get(id = self.chat_session.id).save() # Update ChatSession TimeStampe 53 | 54 | @staticmethod 55 | def count_overall_unread_msg(user_id): 56 | total_unread_msg = 0 57 | user_all_friends = ChatSession.objects.filter(Q(user1__id = user_id) | Q(user2__id = user_id)) 58 | for ch_session in user_all_friends: 59 | un_read_msg_count = ChatMessage.objects.filter(chat_session = ch_session.id,message_detail__read = False).exclude(user__id = user_id).count() 60 | total_unread_msg += un_read_msg_count 61 | return total_unread_msg 62 | 63 | @staticmethod 64 | def meassage_read_true(message_id): 65 | msg_inst = ChatMessage.objects.filter(id = message_id).first() 66 | msg_inst.message_detail['read'] = True 67 | msg_inst.save(update_fields = ['message_detail',]) 68 | return None 69 | 70 | @staticmethod 71 | def all_msg_read(room_id,user): 72 | all_msg = ChatMessage.objects.filter(chat_session = room_id,message_detail__read = False).exclude(user__username = user) 73 | for msg in all_msg: 74 | msg.message_detail['read'] = True 75 | msg.save(update_fields = ['message_detail',]) 76 | return None 77 | 78 | @staticmethod 79 | def sender_inactive_msg(message_id): 80 | return ChatMessage.objects.filter(id = message_id).update(message_detail__Sclr = True) 81 | 82 | @staticmethod 83 | def receiver_inactive_msg(message_id): 84 | return ChatMessage.objects.filter(id = message_id).update(message_detail__Rclr = True) 85 | -------------------------------------------------------------------------------- /chat_app/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, get_object_or_404 2 | from django.contrib.auth.decorators import login_required 3 | from django.db.models import Q 4 | from .models import * 5 | from django.http.response import HttpResponse, HttpResponseRedirect 6 | from django.contrib import messages 7 | 8 | # def room_name(request): 9 | # return render(request, 'chat/enter_room_name.html') 10 | # def room(request, room_name): 11 | # return render(request, 'chat/chat.html', {'room_name': room_name}) 12 | 13 | def home(request): 14 | unread_msg = ChatMessage.count_overall_unread_msg(request.user.id) 15 | return render(request, 'chat/home.html',{"unread_msg" : unread_msg}) 16 | 17 | @login_required 18 | def create_friend(request): 19 | user_1 = request.user 20 | if request.GET.get('id'): 21 | user2_id = request.GET.get('id') 22 | user_2 = get_object_or_404(User,id = user2_id) 23 | get_create = ChatSession.create_if_not_exists(user_1,user_2) 24 | if get_create: 25 | messages.add_message(request,messages.SUCCESS,f'{user_2.username} successfully added in your chat list!!') 26 | else: 27 | messages.add_message(request,messages.SUCCESS,f'{user_2.username} already added in your chat list!!') 28 | return HttpResponseRedirect('/create_friend') 29 | else: 30 | user_all_friends = ChatSession.objects.filter(Q(user1 = user_1) | Q(user2 = user_1)) 31 | user_list = [] 32 | for ch_session in user_all_friends: 33 | user_list.append(ch_session.user1.id) 34 | user_list.append(ch_session.user2.id) 35 | all_user = User.objects.exclude(Q(username=user_1.username)|Q(id__in = list(set(user_list)))) 36 | return render(request, 'chat/create_friend.html',{'all_user' : all_user}) 37 | 38 | 39 | @login_required 40 | def friend_list(request): 41 | user_inst = request.user 42 | user_all_friends = ChatSession.objects.filter(Q(user1 = user_inst) | Q(user2 = user_inst)).select_related('user1','user2').order_by('-updated_on') 43 | all_friends = [] 44 | for ch_session in user_all_friends: 45 | user,user_inst = [ch_session.user2,ch_session.user1] if request.user.username == ch_session.user1.username else [ch_session.user1,ch_session.user2] 46 | un_read_msg_count = ChatMessage.objects.filter(chat_session = ch_session.id,message_detail__read = False).exclude(user = user_inst).count() 47 | data = { 48 | "user_name" : user.username, 49 | "room_name" : ch_session.room_group_name, 50 | "un_read_msg_count" : un_read_msg_count, 51 | "status" : user.profile_detail.is_online, 52 | "user_id" : user.id 53 | } 54 | all_friends.append(data) 55 | 56 | return render(request, 'chat/friend_list.html', {'user_list': all_friends}) 57 | 58 | @login_required 59 | def start_chat(request,room_name): 60 | current_user = request.user 61 | try: 62 | check_user = ChatSession.objects.filter(Q(id = room_name[5:])&(Q(user1 = current_user) | Q(user2 = current_user))) 63 | except Exception: 64 | return HttpResponse("Something went wrong!!!") 65 | if check_user.exists(): 66 | chat_user_pair = check_user.first() 67 | opposite_user = chat_user_pair.user2 if chat_user_pair.user1.username == current_user.username else chat_user_pair.user1 68 | fetch_all_message = ChatMessage.objects.filter(chat_session__id = room_name[5:]).order_by('message_detail__timestamp') 69 | return render(request,'chat/start_chat.html',{'room_name' : room_name,'opposite_user' : opposite_user,'fetch_all_message' : fetch_all_message}) 70 | else: 71 | return HttpResponse("You have't permission to chatting with this user!!!") 72 | 73 | def get_last_message(request): 74 | session_id = request.data.get('room_id') 75 | qs = ChatMessage.objects.filter(chat_session__id = session_id)[10] 76 | return qs -------------------------------------------------------------------------------- /django_channel/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for django_channel project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.2.2. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.2/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | 15 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 16 | BASE_DIR = Path(__file__).resolve().parent.parent 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'django-insecure-58+&a=kzdzv$3ds24t5546rgre4t544t5re%dr@#*=_g2svmlciayiu' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | #====================================== IN Built applications ========================== 32 | INSTALLED_APPS = [ 33 | 34 | 'channels', 35 | 36 | 'django.contrib.admin', 37 | 'django.contrib.auth', 38 | 'django.contrib.contenttypes', 39 | 'django.contrib.sessions', 40 | 'django.contrib.messages', 41 | 'django.contrib.staticfiles', 42 | ] 43 | 44 | # ================================= Internal Application ================================= 45 | INSTALLED_APPS += [ 46 | 'chat_app.apps.ChatAppConfig', 47 | ] 48 | 49 | # ================================ External Applications ================================== 50 | INSTALLED_APPS += [ 51 | 'rest_framework', # Only for login purpose 52 | 'django_admin_inline_paginator', 53 | ] 54 | 55 | MIDDLEWARE = [ 56 | 'django.middleware.security.SecurityMiddleware', 57 | 'django.contrib.sessions.middleware.SessionMiddleware', 58 | 59 | 'corsheaders.middleware.CorsMiddleware', 60 | 61 | 'django.middleware.common.CommonMiddleware', 62 | 'django.middleware.csrf.CsrfViewMiddleware', 63 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 64 | 'django.contrib.messages.middleware.MessageMiddleware', 65 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 66 | ] 67 | 68 | ROOT_URLCONF = 'django_channel.urls' 69 | 70 | TEMPLATES = [ 71 | { 72 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 73 | 'DIRS': [], 74 | 'APP_DIRS': True, 75 | 'OPTIONS': { 76 | 'context_processors': [ 77 | 'django.template.context_processors.debug', 78 | 'django.template.context_processors.request', 79 | 'django.contrib.auth.context_processors.auth', 80 | 'django.contrib.messages.context_processors.messages', 81 | ], 82 | }, 83 | }, 84 | ] 85 | 86 | WSGI_APPLICATION = 'django_channel.wsgi.application' 87 | ASGI_APPLICATION = "django_channel.asgi.application" 88 | 89 | 90 | # Database 91 | # https://docs.djangoproject.com/en/3.2/ref/settings/#databases 92 | 93 | DATABASES = { 94 | 'default': { 95 | 'ENGINE': 'django.db.backends.postgresql', 96 | 'NAME': 'DB_NAME', 97 | 'USER' : 'USER_NAME', 98 | 'PASSWORD' : '****', 99 | 'HOST' : 'localhost', 100 | 'PORT': '5432' 101 | } 102 | } 103 | 104 | 105 | # Password validation 106 | # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators 107 | 108 | AUTH_PASSWORD_VALIDATORS = [ 109 | { 110 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 111 | }, 112 | { 113 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 114 | }, 115 | { 116 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 117 | }, 118 | { 119 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 120 | }, 121 | ] 122 | 123 | 124 | # Internationalization 125 | # https://docs.djangoproject.com/en/3.2/topics/i18n/ 126 | 127 | LANGUAGE_CODE = 'en-us' 128 | 129 | TIME_ZONE = 'ASIA/KOLKATA' 130 | 131 | USE_I18N = True 132 | 133 | USE_L10N = True 134 | 135 | USE_TZ = True 136 | 137 | 138 | # Static files (CSS, JavaScript, Images) 139 | STATIC_URL = '/static/' 140 | 141 | # Default primary key field type 142 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 143 | 144 | 145 | # ================================= Channel Settings =========================== 146 | CHANNEL_LAYERS = { 147 | 'default': { 148 | 'BACKEND': 'channels_redis.core.RedisChannelLayer', 149 | 'CONFIG': { 150 | "hosts": [('127.0.0.1', 6379)], 151 | }, 152 | }, 153 | } 154 | 155 | 156 | LOGIN_URL = '/api_auth/login' 157 | LOGOUT_REDIRECT_URL = '/' 158 | LOGIN_REDIRECT_URL = '/' 159 | -------------------------------------------------------------------------------- /chat_app/templates/chat/start_chat.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Chat Room 7 | 49 | 50 | 51 | 52 | {% load convert_date %} 53 |

🧒 | {{opposite_user.username | title}} 54 |

55 |
56 |
57 | {% for msg in fetch_all_message %} 58 |

59 | {{msg.user.username}} - {{msg.message_detail.timestamp | convert_date | date:"M d'Y f"}} 60 |
61 | • {{msg.message_detail.msg}} 62 |
63 | {% if msg.user == request.user %} 64 | ✔✔ 65 | {% endif %} 66 |

67 | {% endfor %} 68 |

69 | 70 | 71 |
72 | {{ room_name|json_script:"room_name" }} 73 | 74 | 75 | 76 | 77 | 78 | 232 | -------------------------------------------------------------------------------- /chat_app/consumers.py: -------------------------------------------------------------------------------- 1 | from channels.generic.websocket import AsyncWebsocketConsumer 2 | import json 3 | from datetime import datetime 4 | from chat_app.models import ChatSession, ChatMessage 5 | from channels.db import database_sync_to_async 6 | import uuid 7 | from .models import Profile 8 | from django.db.models import Q 9 | 10 | 11 | MESSAGE_MAX_LENGTH = 10 12 | 13 | MESSAGE_ERROR_TYPE = { 14 | "MESSAGE_OUT_OF_LENGTH": 'MESSAGE_OUT_OF_LENGTH', 15 | "UN_AUTHENTICATED": 'UN_AUTHENTICATED', 16 | "INVALID_MESSAGE": 'INVALID_MESSAGE', 17 | } 18 | 19 | MESSAGE_TYPE = { 20 | "WENT_ONLINE": 'WENT_ONLINE', 21 | "WENT_OFFLINE": 'WENT_OFFLINE', 22 | "IS_TYPING": 'IS_TYPING', 23 | "NOT_TYPING": 'NOT_TYPING', 24 | "MESSAGE_COUNTER": 'MESSAGE_COUNTER', 25 | "OVERALL_MESSAGE_COUNTER": 'OVERALL_MESSAGE_COUNTER', 26 | "TEXT_MESSAGE": 'TEXT_MESSAGE', 27 | "MESSAGE_READ": 'MESSAGE_READ', 28 | "ALL_MESSAGE_READ": 'ALL_MESSAGE_READ', 29 | "ERROR_OCCURED": 'ERROR_OCCURED' 30 | } 31 | 32 | class PersonalConsumer(AsyncWebsocketConsumer): 33 | async def connect(self): 34 | self.room_name = self.scope['url_route']['kwargs']['room_name'] 35 | self.room_group_name = f'personal__{self.room_name}' 36 | self.user = self.scope['user'] 37 | 38 | await self.channel_layer.group_add( 39 | self.room_group_name, 40 | self.channel_name 41 | ) 42 | 43 | if self.scope["user"].is_authenticated: 44 | await self.accept() 45 | else: 46 | await self.close(code=4001) 47 | 48 | async def disconnect(self, code): 49 | self.set_offline() 50 | await self.channel_layer.group_discard( 51 | self.room_group_name, 52 | self.channel_name 53 | ) 54 | 55 | async def receive(self, text_data): 56 | data = json.loads(text_data) 57 | msg_type = data.get('msg_type') 58 | user_id = data.get('user_id') 59 | 60 | if msg_type == MESSAGE_TYPE['WENT_ONLINE']: 61 | users_room_id = await self.set_online(user_id) 62 | for room_id in users_room_id: 63 | await self.channel_layer.group_send( 64 | f'personal__{room_id}', 65 | { 66 | 'type': 'user_online', 67 | 'user_name' : self.user.username 68 | } 69 | ) 70 | elif msg_type == MESSAGE_TYPE['WENT_OFFLINE']: 71 | users_room_id = await self.set_offline(user_id) 72 | for room_id in users_room_id: 73 | await self.channel_layer.group_send( 74 | f'personal__{room_id}', 75 | { 76 | 'type': 'user_offline', 77 | 'user_name' : self.user.username 78 | } 79 | ) 80 | 81 | async def user_online(self,event): 82 | await self.send(text_data=json.dumps({ 83 | 'msg_type': MESSAGE_TYPE['WENT_ONLINE'], 84 | 'user_name' : event['user_name'] 85 | })) 86 | 87 | async def message_counter(self, event): 88 | overall_unread_msg = await self.count_unread_overall_msg(event['current_user_id']) 89 | await self.send(text_data=json.dumps({ 90 | 'msg_type': MESSAGE_TYPE['MESSAGE_COUNTER'], 91 | 'user_id': event['user_id'], 92 | 'overall_unread_msg' : overall_unread_msg 93 | })) 94 | 95 | async def user_offline(self,event): 96 | await self.send(text_data=json.dumps({ 97 | 'msg_type': MESSAGE_TYPE['WENT_OFFLINE'], 98 | 'user_name' : event['user_name'] 99 | })) 100 | 101 | @database_sync_to_async 102 | def set_online(self,user_id): 103 | Profile.objects.filter(user__id = user_id).update(is_online = True) 104 | user_all_friends = ChatSession.objects.filter(Q(user1 = self.user) | Q(user2 = self.user)) 105 | user_id = [] 106 | for ch_session in user_all_friends: 107 | user_id.append(ch_session.user2.id) if self.user.username == ch_session.user1.username else user_id.append(ch_session.user1.id) 108 | return user_id 109 | 110 | @database_sync_to_async 111 | def set_offline(self,user_id): 112 | Profile.objects.filter(user__id = user_id).update(is_online = False) 113 | user_all_friends = ChatSession.objects.filter(Q(user1 = self.user) | Q(user2 = self.user)) 114 | user_id = [] 115 | for ch_session in user_all_friends: 116 | user_id.append(ch_session.user2.id) if self.user.username == ch_session.user1.username else user_id.append(ch_session.user1.id) 117 | return user_id 118 | 119 | @database_sync_to_async 120 | def count_unread_overall_msg(self,user_id): 121 | return ChatMessage.count_overall_unread_msg(user_id) 122 | 123 | 124 | class ChatConsumer(AsyncWebsocketConsumer): 125 | 126 | async def connect(self): 127 | self.room_name = self.scope['url_route']['kwargs']['room_name'] 128 | self.room_group_name = self.room_name 129 | self.user = self.scope['user'] 130 | 131 | await self.channel_layer.group_add( 132 | self.room_group_name, 133 | self.channel_name 134 | ) 135 | 136 | if self.scope["user"].is_authenticated: 137 | await self.accept() 138 | else: 139 | await self.accept() 140 | await self.send(text_data=json.dumps({ 141 | "msg_type": MESSAGE_TYPE['ERROR_OCCURED'], 142 | "error_message": MESSAGE_ERROR_TYPE["UN_AUTHENTICATED"], 143 | "user": self.user.username, 144 | })) 145 | await self.close(code=4001) 146 | 147 | async def disconnect(self, code): 148 | await self.channel_layer.group_discard( 149 | self.room_group_name, 150 | self.channel_name 151 | ) 152 | 153 | # Receive message from WebSocket 154 | async def receive(self, text_data): 155 | data = json.loads(text_data) 156 | message = data.get('message') 157 | msg_type = data.get('msg_type') 158 | user = data.get('user') 159 | 160 | if msg_type == MESSAGE_TYPE['TEXT_MESSAGE']: 161 | if len(message) <= MESSAGE_MAX_LENGTH: 162 | msg_id = uuid.uuid4() 163 | await self.channel_layer.group_send( 164 | self.room_group_name, 165 | { 166 | 'type': 'chat_message', 167 | 'message': message, 168 | 'user': user, 169 | 'msg_id' : str(msg_id) 170 | } 171 | ) 172 | current_user_id = await self.save_text_message(msg_id,message) 173 | await self.channel_layer.group_send( 174 | f'personal__{current_user_id}', 175 | { 176 | 'type': 'message_counter', 177 | 'user_id' : self.user.id, 178 | 'current_user_id' : current_user_id 179 | } 180 | ) 181 | else: 182 | await self.send(text_data=json.dumps({ 183 | 'msg_type': MESSAGE_TYPE['ERROR_OCCURED'], 184 | 'error_message': MESSAGE_ERROR_TYPE["MESSAGE_OUT_OF_LENGTH"], 185 | 'message': message, 186 | 'user': user, 187 | 'timestampe': str(datetime.now()), 188 | })) 189 | elif msg_type == MESSAGE_TYPE['MESSAGE_READ']: 190 | msg_id = data['msg_id'] 191 | await self.msg_read(msg_id) 192 | await self.channel_layer.group_send( 193 | self.room_group_name, 194 | { 195 | 'type': 'msg_as_read', 196 | 'msg_id': msg_id, 197 | 'user' : user 198 | } 199 | ) 200 | elif msg_type == MESSAGE_TYPE['ALL_MESSAGE_READ']: 201 | await self.channel_layer.group_send( 202 | self.room_group_name, 203 | { 204 | 'type': 'all_msg_read', 205 | 'user' : user, 206 | } 207 | ) 208 | await self.read_all_msg(self.room_name[5:],user) 209 | elif msg_type == MESSAGE_TYPE['IS_TYPING']: 210 | await self.channel_layer.group_send( 211 | self.room_group_name, 212 | { 213 | 'type': 'user_is_typing', 214 | 'user' : user, 215 | } 216 | ) 217 | elif msg_type == MESSAGE_TYPE["NOT_TYPING"]: 218 | await self.channel_layer.group_send( 219 | self.room_group_name, 220 | { 221 | 'type': 'user_not_typing', 222 | 'user' : user, 223 | } 224 | ) 225 | 226 | # Receive message from room group 227 | async def chat_message(self, event): 228 | await self.send(text_data=json.dumps({ 229 | 'msg_type': MESSAGE_TYPE['TEXT_MESSAGE'], 230 | 'message': event['message'], 231 | 'user': event['user'], 232 | 'timestampe': str(datetime.now()), 233 | 'msg_id' : event["msg_id"] 234 | })) 235 | 236 | async def msg_as_read(self,event): 237 | await self.send(text_data=json.dumps({ 238 | 'msg_type': MESSAGE_TYPE['MESSAGE_READ'], 239 | 'msg_id': event['msg_id'], 240 | 'user' : event['user'] 241 | })) 242 | 243 | async def all_msg_read(self,event): 244 | await self.send(text_data=json.dumps({ 245 | 'msg_type': MESSAGE_TYPE['ALL_MESSAGE_READ'], 246 | 'user' : event['user'] 247 | })) 248 | 249 | async def user_is_typing(self,event): 250 | await self.send(text_data=json.dumps({ 251 | 'msg_type': MESSAGE_TYPE['IS_TYPING'], 252 | 'user' : event['user'] 253 | })) 254 | 255 | async def user_not_typing(self,event): 256 | await self.send(text_data=json.dumps({ 257 | 'msg_type': MESSAGE_TYPE['NOT_TYPING'], 258 | 'user' : event['user'] 259 | })) 260 | 261 | @database_sync_to_async 262 | def save_text_message(self,msg_id,message): 263 | session_id = self.room_name[5:] 264 | session_inst = ChatSession.objects.select_related('user1', 'user2').get(id=session_id) 265 | message_json = { 266 | "msg": message, 267 | "read": False, 268 | "timestamp": str(datetime.now()), 269 | session_inst.user1.username: False, 270 | session_inst.user2.username: False 271 | } 272 | ChatMessage.objects.create(id = msg_id,chat_session=session_inst, user=self.user, message_detail=message_json) 273 | return session_inst.user2.id if self.user == session_inst.user1 else session_inst.user1.id 274 | 275 | @database_sync_to_async 276 | def msg_read(self,msg_id): 277 | return ChatMessage.meassage_read_true(msg_id) 278 | 279 | @database_sync_to_async 280 | def read_all_msg(self,room_id,user): 281 | return ChatMessage.all_msg_read(room_id,user) --------------------------------------------------------------------------------