from channels.generic.websocket import WebsocketConsumer from .models import * import json from django.template.loader import render_to_string from asgiref.sync import async_to_sync from django.shortcuts import get_object_or_404 import requests from django.forms.models import model_to_dict from django.core.serializers.json import DjangoJSONEncoder from django.db.models import Case, When, F, Max, DateTimeField from django.db import transaction class OsitcomVisitor(WebsocketConsumer): def connect(self): async_to_sync(self.channel_layer.group_add)( 'ositcom_visitors', self.channel_name ) self.accept() self.visitor = None def disconnect(self, close_code): if self.visitor: self.current_log.left_date = datetime.now() self.current_log.save() async_to_sync(self.channel_layer.group_discard)( 'ositcom_visitors', self.channel_name ) def receive(self, text_data): text_data_json = json.loads(text_data) event_type = text_data_json.get('event_type') if event_type == 'visitor_ping': session_id = text_data_json.get('session_id') if Visitor.objects.filter(session_id=session_id).last(): self.visitor = Visitor.objects.filter(session_id=session_id).last() else: self.visitor = Visitor.objects.create( session_id = session_id, ip_address = text_data_json.get('client_ip'), country = text_data_json.get('client_country'), region = text_data_json.get('client_region'), browser_name = text_data_json.get('browser_name'), os_name = text_data_json.get('os_name'), ) self.current_log = VisitorLog.objects.create( visitor = self.visitor, referrer = text_data_json.get('referrer'), title = text_data_json.get('title'), url = text_data_json.get('url'), visit_date = datetime.now() ) class Osichat(WebsocketConsumer): def connect(self): async_to_sync(self.channel_layer.group_add)( 'osichat', self.channel_name ) self.accept() def disconnect(self, close_code): async_to_sync(self.channel_layer.group_discard)( 'osichat', self.channel_name ) def receive(self, text_data): data = json.loads(text_data) event_type = data.get('event_type') if event_type == 'set_client_type': self.client_type = data.get('client_type') self.user_id = data.get('user_id') event = { 'type': 'get_chats_handler', } if data.get('whereAmI') == 'Chats': self.get_chats_handler(event) else: self.get_visitors_handler(event) if event_type == 'get_chats': event = { 'type': 'get_chats_handler', } self.get_chats_handler(event) if event_type == 'get_visitors': event = { 'type': 'get_visitors_handler', } self.get_visitors_handler(event) def get_chats_handler(self, event): chat_rooms = ChatRoom.objects.annotate(last_update=Max('chatmessage__date_sent'), order_key=Case( When(last_update__isnull=True, then=F('date_created')), default=F('last_update'), output_field=DateTimeField(),)).order_by('-order_key') user = get_object_or_404(User, id=self.user_id) for room in chat_rooms: room.number_of_unread = room.unread_messages(user) context = { 'chat_rooms': chat_rooms, } if self.client_type == 'mobile_admin': chat_rooms_data = [] for chat_room in chat_rooms: last_message = ChatMessage.objects.filter(room=chat_room).last() room_data = model_to_dict(chat_room) room_data['number_of_unread'] = chat_room.unread_messages(user) if last_message: room_data['last_message'] = model_to_dict(last_message) else: room_data['last_message'] = None if hasattr(chat_room, 'chatroomguest') and chat_room.chatroomguest.visitor: visitor = chat_room.chatroomguest.visitor room_data['visitor'] = { 'id': visitor.id, 'session_id': visitor.session_id, 'ip': visitor.ip_address, 'country': visitor.country, 'region' : visitor.region, 'country_flag': visitor.flag_image_url, 'name': visitor.name, 'mobile_number': visitor.mobile_number, } chat_rooms_data.append(room_data) self.send(text_data=json.dumps({ 'event_type': 'get_chats', 'chat_rooms_data': chat_rooms_data, }, cls=DjangoJSONEncoder)) else: html = render_to_string("chat_templates/partials/rooms.html", context=context) self.send(text_data=json.dumps({ 'event_type': 'get_chats', 'html': html, })) def new_chat_update_handler(self, event): chat_room = get_object_or_404(ChatRoom, id=event['chatroom_id']) user = get_object_or_404(User, id=self.user_id) number_of_unread = ChatMessage.objects.filter(room=chat_room).exclude(member=user).exclude(chatmessageseen__member=user).count() last_message = ChatMessage.objects.filter(room=chat_room).last() context = { 'chat_room': chat_room, 'number_of_unread': number_of_unread } if self.client_type == 'mobile_admin': chat_room_data = model_to_dict(chat_room) chat_room_data['visitor'] = model_to_dict(chat_room.chatroomguest.visitor) if last_message: chat_room_data['last_message'] = model_to_dict(last_message) else: chat_room_data['last_message'] = None self.send(text_data=json.dumps({ 'event_type': 'new_chat_update', 'chat_room_data': chat_room_data, }, cls=DjangoJSONEncoder)) else: html = render_to_string("chat_templates/partials/new-chat-room.html", context=context) self.send(text_data=json.dumps({ 'event_type': 'new_chat_update', 'chatroom_id': chat_room.id, 'user': last_message.member.id if last_message and last_message.member else None, 'html': html, })) def get_visitors_handler(self, event): today = timezone.now().date() visitors = Visitor.objects.filter(visitorlog__visit_date__date=today).annotate(latest_visit=Max('visitorlog__visit_date')).order_by('-latest_visit') context = { 'visitors': visitors, } if self.client_type == 'mobile_admin': visitors_data = [] for visitor in visitors: visitor_data = model_to_dict(visitor) visitor_data['is_online'] = visitor.is_online visitor_data['duration'] = visitor.total_duration visitor_data['flag'] = visitor.flag_image_url visitor_logs = VisitorLog.objects.filter(visitor=visitor) visitor_logs_data = [] for log in visitor_logs: log_data = model_to_dict(log) log_data['log_duration'] = log.log_duration visitor_logs_data.append(log_data) visitor_data['visitor_logs'] = visitor_logs_data visitors_data.append(visitor_data) self.send(text_data=json.dumps({ 'event_type': 'get_visitors', 'visitors_data': visitors_data, }, cls=DjangoJSONEncoder)) else: html = render_to_string("chat_templates/partials/visitors.html", context=context) self.send(text_data=json.dumps({ 'event_type': 'get_visitors', 'html': html, })) def new_visitor_update_handler(self, event): visitor = get_object_or_404(Visitor, id=event['visitor_id']) latest_log = VisitorLog.objects.filter(visitor=visitor).last() context = { 'visitor': visitor, } if self.client_type == 'mobile_admin': visitor_data = model_to_dict(visitor) visitor_data['is_online'] = visitor.is_online visitor_data['duration'] = visitor.total_duration visitor_data['flag'] = visitor.flag_image_url if latest_log: latest_log_data = model_to_dict(latest_log) latest_log_data['log_duration'] = latest_log.log_duration visitor_data['latest_log'] = latest_log_data self.send(text_data=json.dumps({ 'event_type': 'new_visitor_update', 'visitor_data': visitor_data, 'action': event['action'], }, cls=DjangoJSONEncoder)) else: html = render_to_string("chat_templates/partials/new-visitor.html", context=context) self.send(text_data=json.dumps({ 'event_type': 'new_visitor_update', 'visitor_id': visitor.id, 'action': event['action'], 'html': html, })) class OsichatVisitor(WebsocketConsumer): def connect(self): self.visitor_id = self.scope['url_route']['kwargs']['visitor_id'] self.group_name = 'V' + str(self.visitor_id) self.visitor = get_object_or_404(Visitor, id=self.visitor_id) async_to_sync(self.channel_layer.group_add)( self.group_name, self.channel_name ) self.accept() def disconnect(self, close_code): async_to_sync(self.channel_layer.group_discard)( self.group_name, self.channel_name ) def receive(self, text_data): data = json.loads(text_data) event_type = data.get('event_type') if event_type == 'set_client_type': self.client_type = data.get('client_type') event = { 'type': 'get_visitor_handler', } async_to_sync(self.channel_layer.group_send)( self.group_name, event ) def get_visitor_handler(self, event): if self.client_type == 'mobile_admin': visitor_data = model_to_dict(self.visitor) visitor_data['online'] = self.visitor.is_online visitor_data['flag'] = self.visitor.flag_image_url visitor_logs = VisitorLog.objects.filter(visitor=self.visitor) visitor_logs_data = [] for log in visitor_logs: log_data = model_to_dict(log) log_data['log_duration'] = log.log_duration visitor_logs_data.append(log_data) visitor_data['visitor_logs'] = visitor_logs_data self.send(text_data=json.dumps({ 'event_type': 'get_visitor', 'visitor_data': visitor_data, }, cls=DjangoJSONEncoder)) def new_visitor_update_handler(self, event): visitor = self.visitor latest_log = VisitorLog.objects.filter(visitor=self.visitor).last() if self.client_type == 'mobile_admin': visitor_data = model_to_dict(visitor) visitor_data['is_online'] = visitor.is_online visitor_data['duration'] = visitor.total_duration visitor_data['flag'] = visitor.flag_image_url if latest_log: latest_log_data = model_to_dict(latest_log) latest_log_data['log_duration'] = latest_log.log_duration visitor_data['latest_log'] = latest_log_data self.send(text_data=json.dumps({ 'event_type': 'new_visitor_update', 'visitor_data': visitor_data, 'action': event['action'], }, cls=DjangoJSONEncoder)) class OsitcomChatRoom(WebsocketConsumer): def connect(self): self.domain = 'https://osina.ositcom.com' self.session_id = self.scope['url_route']['kwargs']['session_id'] self.visitor = Visitor.objects.filter(session_id=self.session_id).last() if not self.visitor: self.close() if self.scope['url_route']['kwargs'].get('chat_id'): #Case where admin is accessing a specific conversation between the conversations of this visitor self.chat_room = get_object_or_404(ChatRoom, id=self.scope['url_route']['kwargs'].get('chat_id')) else: chat_room_guest = ChatRoomGuest.objects.filter(visitor=self.visitor).last() #Case where the visitor will always acesss his last conversation if chat_room_guest: self.chat_room = chat_room_guest.room else: self.chat_room = None if self.chat_room: self.group = f"{self.session_id}_{self.chat_room.id}" else: self.group = self.session_id async_to_sync(self.channel_layer.group_add)( self.group, self.channel_name ) self.accept() def disconnect(self, close_code): async_to_sync(self.channel_layer.group_discard)( self.group, self.channel_name ) def receive(self, text_data): text_data_json = json.loads(text_data) event_type = text_data_json.get('event_type') if event_type == 'load_chat': self.client_type = text_data_json.get('client_type') event = { 'reconnecting' : text_data_json.get('reconnecting') } self.load_chat_handler(event) if event_type == 'start_conversation': try: with transaction.atomic(): chat_room = ChatRoom.objects.create( name=f"Support: {self.session_id}", date_created = datetime.now() ) if text_data_json.get('guest_name'): self.visitor.name = text_data_json.get('guest_name') self.visitor.save() if text_data_json.get('guest_mobile_number'): self.visitor.mobile_number = text_data_json.get('guest_mobile_number') self.visitor.save() chat_room_guest = ChatRoomGuest.objects.create( room=chat_room, visitor=self.visitor ) self.chat_room = chat_room async_to_sync(self.channel_layer.group_discard)( self.group, self.channel_name ) self.group = f"{self.session_id}_{self.chat_room.id}" async_to_sync(self.channel_layer.group_add)( self.group, self.channel_name ) event = { 'type': 'start_conversation_handler', 'chat_room_id': chat_room.id } async_to_sync(self.channel_layer.group_send)( self.group, event ) except Exception as e: print('Error starting conversation') if event_type == 'typing': event = { 'type': 'typing_handler', 'user_id': text_data_json.get('user_id'), 'typing_status': text_data_json.get('typing_status') } async_to_sync(self.channel_layer.group_send)( self.group, event ) if event_type == 'send_message': if text_data_json.get('user_id'): member = get_object_or_404(User, id=text_data_json.get('user_id')) else: member = None chat_message = ChatMessage.objects.create( member = member, room = self.chat_room, content= text_data_json.get('message'), date_sent = datetime.now() ) event = { 'type': 'send_message_handler', 'chat_message_id': chat_message.id } async_to_sync(self.channel_layer.group_send)( self.group, event ) if event_type == 'uploaded_file': if text_data_json.get('user_id'): member = get_object_or_404(User, id=text_data_json.get('user_id')) else: member = None message = ChatMessage.objects.create( member = member, room = self.chat_room, date_sent = datetime.now() ) message_attachment = ChatMessageAttachment.objects.create( message = message, attachment = text_data_json.get('path'), ) event = { 'type': 'uploaded_file_handler', 'message_attachment_id': message_attachment.id, 'file_type': text_data_json.get('file_type'), 'file_name': text_data_json.get('file_name'), } async_to_sync(self.channel_layer.group_send)( self.group, event ) if event_type == 'update_read_messages' and self.chat_room: latest_unread_message = None number_of_unread = 0 if text_data_json.get('user_id'): member = get_object_or_404(User, id=text_data_json.get('user_id')) guest = None if text_data_json.get('chat_state') == 'open': messages = ChatMessage.objects.filter(room=self.chat_room).exclude(member=member) for message in messages: seen_message = ChatMessageSeen.objects.filter( message=message, member=member ).exists() if not seen_message: ChatMessageSeen.objects.create( message=message, member=member, seen_date = datetime.now() ) number_of_unread = 0 else: number_of_unread = ChatMessage.objects.filter(room=self.chat_room).exclude(member = member).exclude(hatmessageseen__member=member).count() latest_unread_message = ChatMessage.objects.filter(room=self.chat_room).exclude(chatmessageseen__member=member).last() else: member = None guest = self.chat_room.chatroomguest if text_data_json.get('chat_state') == 'open': messages = ChatMessage.objects.filter(room=self.chat_room).exclude(member=None) for message in messages: seen_message = ChatMessageSeen.objects.filter( message=message, guest=guest ).exists() if not seen_message: ChatMessageSeen.objects.create( message=message, guest=guest, seen_date = datetime.now() ) number_of_unread = 0 else: number_of_unread = ChatMessage.objects.filter(room=self.chat_room, member__isnull=False).exclude(chatmessageseen__guest=guest).count() latest_unread_message = ChatMessage.objects.filter(room=self.chat_room, member__isnull=False).exclude(chatmessageseen__guest=guest).last() event = { 'type': 'update_read_messages_handler', 'number_of_unread': number_of_unread, 'latest_unread_message_id': latest_unread_message.id if latest_unread_message else None } self.update_read_messages_handler(event) if event_type == 'end_chat': event = { 'type': 'end_chat_handler', 'user_id': text_data_json.get('user_id') } async_to_sync(self.channel_layer.group_send)( self.group, event ) if event_type == 'submit_review': if ChatRoomReview.objects.filter(room=self.chat_room).last(): review = ChatRoomReview.objects.filter(room=self.chat_room).last() if text_data_json.get('reaction'): review.reaction = text_data_json.get('reaction') if text_data_json.get('details'): review.details = text_data_json.get('details') review.save() else: review = ChatRoomReview.objects.create( room = self.chat_room, reaction = text_data_json.get('reaction'), details = text_data_json.get('details'), ) event = { 'type': 'submit_review_handler', 'review_id': review.id } async_to_sync(self.channel_layer.group_send)( self.group, event ) def load_chat_handler(self, event): if self.chat_room: chat_room = self.chat_room chat_room_messages = ChatMessage.objects.filter(room=chat_room).order_by('date_sent') review = ChatRoomReview.objects.filter(room=chat_room).last() else: chat_room = None chat_room_messages = None review = None context = { 'chat_room': chat_room, 'chat_room_messages': chat_room_messages, 'review': review, 'domain': self.domain } if self.client_type == 'mobile_admin': chat_room_data = model_to_dict(chat_room) chat_room_messages_data = [] for message in chat_room_messages: message_data = model_to_dict(message) attachment = getattr(message, 'chatmessageattachment', None) member = message.member if member: message_data['member'] = { 'id': member.id, 'first_name': member.first_name, 'last_name': member.last_name, 'image': member.staffprofile.image.url if member.staffprofile and member.staffprofile.image else None } if attachment: message_data['attachment'] = { 'attachment': attachment.attachment, 'is_image': attachment.is_image(), 'file_name': attachment.file_name, } else: message_data['attachment'] = None chat_room_messages_data.append(message_data) self.send(text_data=json.dumps({ 'event_type': 'load_chat', 'chat_room_data': chat_room_data, 'chat_room_messages_data': chat_room_messages_data, }, cls=DjangoJSONEncoder)) elif self.client_type == 'website_admin': html = render_to_string("chat_templates/chat-room.html", context=context) self.send(text_data=json.dumps({ 'event_type': 'load_chat', 'html': html, })) else: if event.get('reconnecting') == 'False': #Connecting for the first time html = render_to_string("chat-widget.html", context=context) else: if chat_room: if chat_room.date_terminated: html = render_to_string("ended-chat.html", context=context) else: html = render_to_string("chat-room.html", context=context) else: html = render_to_string("start-chat.html", context=context) self.send(text_data=json.dumps({ 'event_type': 'load_chat', 'html': html, })) def start_conversation_handler(self, event): chat_room = get_object_or_404(ChatRoom, id=event['chat_room_id']) context = { 'chat_room': chat_room, 'session_id':self.session_id, 'domain': self.domain } if self.client_type == 'mobile_admin': self.send(text_data=json.dumps({ 'event_type': 'start_conversation', 'context': context, })) else: html = render_to_string("chat-room.html", context=context) self.send(text_data=json.dumps({ 'event_type': 'start_conversation', 'html': html, })) def typing_handler(self, event): if event.get('typing_status') == 'typing': if event.get('user_id'): member = get_object_or_404(User, id=event.get('user_id')) else: member = None context = { 'member': member, 'chat_room': self.chat_room, 'domain': self.domain } if self.client_type == 'mobile_admin': member_data = None if member: member_data = model_to_dict(member) member_data['image'] = member.staffprofile.image.url if member.staffprofile and member.staffprofile.image else None self.send(text_data=json.dumps({ 'event_type': 'typing', 'member_data': member_data, }, cls=DjangoJSONEncoder)) elif self.client_type == 'website_admin': html = render_to_string("chat_templates/partials/typing.html", context=context) self.send(text_data=json.dumps({ 'event_type': 'typing', 'user': member.id if member else None, 'html': html, })) else: html = render_to_string("partials/typing.html", context=context) self.send(text_data=json.dumps({ 'event_type': 'typing', 'user': member.id if member else None, 'html': html, })) else: self.send(text_data=json.dumps({ 'event_type': 'stopped_typing', })) def send_message_handler(self, event): chat_message = get_object_or_404(ChatMessage, id=event['chat_message_id']) context = { 'chat_message': chat_message, 'domain': self.domain } if self.client_type == 'mobile_admin': chat_message_data = model_to_dict(chat_message) member = chat_message.member if member: chat_message_data['member'] = { 'id': member.id, 'first_name': member.first_name, 'last_name': member.last_name, 'image': member.staffprofile.image.url if member.staffprofile and member.staffprofile.image else None } self.send(text_data=json.dumps({ 'event_type': 'send_message', 'chat_message_data': chat_message_data, },cls=DjangoJSONEncoder)) elif self.client_type == 'website_admin': html = render_to_string("chat_templates/partials/message.html", context=context) self.send(text_data=json.dumps({ 'event_type': 'send_message', 'user': chat_message.member.id if chat_message.member else None, 'html': html, })) else: html = render_to_string("partials/message.html", context=context) self.send(text_data=json.dumps({ 'event_type': 'send_message', 'user': chat_message.member.id if chat_message.member else None, 'html': html, })) def uploaded_file_handler(self, event): message_attachment = get_object_or_404(ChatMessageAttachment, id=event['message_attachment_id']) context = { 'message_attachment': message_attachment, 'file_type': event['file_type'], 'domain': self.domain } if self.client_type == 'mobile_admin': message_attachment_data = model_to_dict(message_attachment) self.send(text_data=json.dumps({ 'event_type': 'uploaded_file', 'message_attachment_data': message_attachment_data, 'user': message_attachment.message.member.id if message_attachment.message.member else None, 'file_type': event['file_type'] },cls=DjangoJSONEncoder)) else: html = render_to_string("partials/message-attachment.html", context=context) self.send(text_data=json.dumps({ 'event_type': 'uploaded_file', 'file_name': event['file_name'], 'user': message_attachment.message.member.id if message_attachment.message.member else None, 'html': html, })) def update_read_messages_handler(self, event): latest_unread_message_id = event.get('latest_unread_message_id') if latest_unread_message_id: latest_unread_message = get_object_or_404(ChatMessage, id=latest_unread_message_id) context = { 'number_of_unread' : event['number_of_unread'], 'latest_unread_message': latest_unread_message, 'domain': self.domain } if self.client_type == 'mobile_admin': self.send(text_data=json.dumps({ 'event_type': 'update_read_messages', 'number_of_unread': event['number_of_unread'], },cls=DjangoJSONEncoder)) else: html = render_to_string("partials/unread-messages.html", context=context) self.send(text_data=json.dumps({ 'event_type': 'update_read_messages', 'html': html, })) else: latest_unread_message = None def end_chat_handler(self, event): if event['user_id']: member = get_object_or_404(User, id=event['user_id']) else: member = None self.chat_room.date_terminated = datetime.now() self.chat_room.terminated_by = member self.chat_room.save() context = { 'chat_room': self.chat_room, 'chat_room_messages': ChatMessage.objects.filter(room=self.chat_room).order_by('date_sent'), 'domain': self.domain } html = render_to_string("ended-chat.html", context=context) self.send(text_data=json.dumps({ 'event_type': 'ended_chat', 'html': html, })) def submit_review_handler(self, event): review = get_object_or_404(ChatRoomReview, id=event['review_id']) context = { 'review': review, 'chat_room': self.chat_room, } html = render_to_string("partials/submitted-review.html", context=context) self.send(text_data=json.dumps({ 'event_type': 'submit_review', 'html': html, }))