|
1 | | -# The below code is just for testing purposes (it works!) |
2 | | -# Tested it using https://websocketking.com/ |
3 | | -# and entered ws://localhost:8000/ws/apps/chat/practice/ |
4 | 1 | import json |
5 | 2 | from channels.generic.websocket import AsyncWebsocketConsumer |
6 | 3 | from asgiref.sync import sync_to_async |
@@ -33,35 +30,111 @@ async def disconnect(self, close_code): |
33 | 30 |
|
34 | 31 | # This function receive messages from WebSocket. |
35 | 32 | async def receive(self, text_data): |
| 33 | + # Message is received |
36 | 34 | text_data_json = json.loads(text_data) |
37 | | - message = text_data_json['message'] |
| 35 | + if 'message' in text_data_json: |
| 36 | + message = text_data_json['message'] |
38 | 37 |
|
39 | | - # Create a new message object |
40 | | - username = await self.save_message(self.user_id, self.room_name, message) |
| 38 | + # Create a new message object |
| 39 | + username, id, timestamp = await self.save_message(self.user_id, self.room_name, message) |
41 | 40 |
|
42 | | - await self.channel_layer.group_send( # sends message to all users in a channel group |
43 | | - self.room_group_name, |
44 | | - { |
45 | | - 'type': 'chat_message', # Calls chat_message() |
46 | | - 'message': message, |
47 | | - 'username': username |
48 | | - } |
49 | | - ) |
| 41 | + # Serialize timestamp |
| 42 | + formatted = timestamp.isoformat() |
| 43 | + |
| 44 | + await self.channel_layer.group_send( # sends message to all users in a channel group |
| 45 | + self.room_group_name, |
| 46 | + { |
| 47 | + 'type': 'chat_message', # Calls chat_message() |
| 48 | + 'message': message, |
| 49 | + 'username': username, |
| 50 | + 'id': id, |
| 51 | + 'timestamp': formatted |
| 52 | + } |
| 53 | + ) |
| 54 | + |
| 55 | + # Typing status is received |
| 56 | + elif 'typing' in text_data_json: |
| 57 | + typing = text_data_json['typing'] |
| 58 | + username = await self.get_username(self.user_id) |
| 59 | + await self.channel_layer.group_send( # sends message to all users in a channel group |
| 60 | + self.room_group_name, |
| 61 | + { |
| 62 | + 'type': 'typing_status', # Calls typing_status() |
| 63 | + 'typing': typing, |
| 64 | + 'username': username |
| 65 | + } |
| 66 | + ) |
| 67 | + # Seen status is received |
| 68 | + elif 'seen' in text_data_json: |
| 69 | + sender_username, message_ids = await self.mark_as_seen(self.room_name, self.user_id) |
| 70 | + |
| 71 | + await self.channel_layer.group_send( # sends message to all users in a channel group |
| 72 | + self.room_group_name, |
| 73 | + { |
| 74 | + 'type': 'seen_status', # Calls seen_status() |
| 75 | + 'username': sender_username, |
| 76 | + 'ids': message_ids |
| 77 | + } |
| 78 | + ) |
| 79 | + |
| 80 | + @sync_to_async |
| 81 | + def mark_as_seen(self, room_name, user_id): |
| 82 | + chat = Chat.objects.get(name = room_name) |
| 83 | + # Get all unseen messages (excluding those from the receiver) |
| 84 | + unseen_messages = chat.messages.filter(status=Message.NOT_SEEN).exclude(sender_id=user_id).order_by('-timestamp') |
| 85 | + |
| 86 | + # Get sender (assumes all chats only have 2 people) |
| 87 | + first_unseen = unseen_messages.first() |
| 88 | + sender_username = None |
| 89 | + |
| 90 | + # If there are unseen messages from sender |
| 91 | + if first_unseen: |
| 92 | + sender_username = first_unseen.sender.username |
| 93 | + |
| 94 | + # Get message ids (maintaining order) |
| 95 | + message_ids = list(unseen_messages.values_list('id', flat=True)) |
| 96 | + |
| 97 | + # Mark unseen messages as seen |
| 98 | + unseen_messages.update(status=Message.SEEN) |
| 99 | + |
| 100 | + return sender_username, message_ids |
| 101 | + |
| 102 | + |
| 103 | + async def seen_status(self, event): |
| 104 | + await self.send(text_data=json.dumps({ |
| 105 | + 'message': 'seen_successful', |
| 106 | + 'ids': event['ids'], |
| 107 | + 'username': event['username'] |
| 108 | + })) |
| 109 | + |
| 110 | + async def typing_status(self, event): |
| 111 | + await self.send(text_data=json.dumps({ |
| 112 | + 'message': 'successful', |
| 113 | + 'typing': event['typing'], |
| 114 | + 'username': event['username'] |
| 115 | + })) |
50 | 116 |
|
51 | 117 | async def chat_message(self, event): |
52 | 118 | await self.send(text_data=json.dumps({ |
53 | 119 | 'message': 'successful', |
54 | 120 | 'body': event['message'], |
55 | | - 'username': event['username'] |
| 121 | + 'username': event['username'], |
| 122 | + 'id': event['id'], |
| 123 | + 'timestamp': event['timestamp'] |
56 | 124 | })) |
57 | 125 |
|
| 126 | + @sync_to_async |
| 127 | + def get_username(self, id): |
| 128 | + user = User.objects.get(id=id) |
| 129 | + return user.username |
| 130 | + |
58 | 131 | @sync_to_async |
59 | 132 | def save_message(self, id, room_name, content): |
60 | 133 | try: |
61 | 134 | user = User.objects.get(id=id) |
62 | 135 | chat = Chat.objects.get(name = room_name) |
63 | | - Message.objects.create(content=content, chat = chat, sender = user) # Creates and saves message |
64 | | - return user.username |
| 136 | + message = Message.objects.create(content=content, chat = chat, sender = user) # Creates and saves message |
| 137 | + return user.username, message.id, message.timestamp |
65 | 138 | except (Chat.DoesNotExist): |
66 | 139 | print(f"Chat room '{room_name}' not found. Message not saved.") |
67 | 140 | return "Unknown" |
0 commit comments