1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
|
from channels.db import database_sync_to_async
from channels.generic.websocket import AsyncJsonWebsocketConsumer
from core.letters.functions import (
find_user_by_username,
get_conversation_by_users,
get_letter_attachments,
get_or_create_conversation,
has_pending_attachments,
mark_letters_read,
send_letter,
)
class ConversationConsumer(AsyncJsonWebsocketConsumer):
async def connect(self):
self.user = self.scope["user"]
if self.user.is_anonymous:
await self.close()
return
self.other_username = self.scope["url_route"]["kwargs"]["username"]
self.other_user = await self._find_other_user()
if not self.other_user:
await self.close()
return
self.conversation = await database_sync_to_async(get_conversation_by_users)(
self.user, self.other_user
)
if self.conversation:
self.room_group = f"letters_{self.conversation.pk}"
await self.channel_layer.group_add(self.room_group, self.channel_name)
await self.accept()
async def disconnect(self, close_code):
if hasattr(self, "room_group"):
await self.channel_layer.group_discard(
self.room_group, self.channel_name
)
async def receive_json(self, content):
msg_type = content.get("type")
if msg_type == "letter.send":
await self._handle_send(content)
elif msg_type == "letter.read":
if self.conversation:
await self._handle_read()
async def _handle_send(self, content):
text = content.get("content", "").strip()
if not self.conversation:
success, conv = await database_sync_to_async(get_or_create_conversation)(
self.user, self.other_user
)
if not success:
return
self.conversation = conv
self.room_group = f"letters_{self.conversation.pk}"
await self.channel_layer.group_add(self.room_group, self.channel_name)
has_attachments = await database_sync_to_async(has_pending_attachments)(
self.user, self.conversation
)
if not text and not has_attachments:
return
result = await database_sync_to_async(send_letter)(
self.user, self.conversation, text
)
if not result[0]:
return
letter = result[1]
attachments = await database_sync_to_async(get_letter_attachments)(letter)
await self.channel_layer.group_send(
self.room_group,
{
"type": "letter.new",
"letter_id": letter.pk,
"sender": self.user.username,
"content": letter.content,
"created_at": letter.created_at.isoformat(),
"attachments": attachments,
},
)
async def _handle_read(self):
await database_sync_to_async(mark_letters_read)(
self.user, self.conversation
)
await self.channel_layer.group_send(
self.room_group,
{
"type": "letter.read_receipt",
"reader": self.user.username,
},
)
# --- group message handlers ---
async def letter_new(self, event):
await self.send_json(
{
"type": "letter.new",
"letter_id": event["letter_id"],
"sender": event["sender"],
"content": event["content"],
"created_at": event["created_at"],
"attachments": event.get("attachments", []),
}
)
async def letter_read_receipt(self, event):
await self.send_json(
{
"type": "letter.read_receipt",
"reader": event["reader"],
}
)
# --- db helpers ---
@database_sync_to_async
def _find_other_user(self):
success, result = find_user_by_username(self.other_username)
if not success:
return None
if self.user == result:
return None
return result
|