|
1
|
|
|
#-*- coding = utf-8 -*- |
|
2
|
|
|
import tornado.web |
|
3
|
|
|
import json |
|
4
|
|
|
from tornado import websocket |
|
5
|
|
|
|
|
6
|
|
|
# General modules. |
|
7
|
|
|
import logging |
|
8
|
|
|
|
|
9
|
|
|
""" |
|
10
|
|
|
Django send messages to all the others |
|
11
|
|
|
A client has a WebSocketConection with this process |
|
12
|
|
|
This connection has to be authorised -> Token/other : how to check that the user is authorised ? ChatHandler has a list of authorized tokens |
|
13
|
|
|
Connections only accept messages from Django |
|
14
|
|
|
|
|
15
|
|
|
Proccess : |
|
16
|
|
|
- User logs in -> Django send token to MainHandler |
|
17
|
|
|
- User sent message -> Django save it and give it to the MainHandler, which gives it to the appropriated connections -> clients |
|
18
|
|
|
- User logs out -> Django tell MainHandler to remove the token from the list |
|
19
|
|
|
""" |
|
20
|
|
|
class MainHandler(tornado.web.RequestHandler): |
|
21
|
|
|
|
|
22
|
|
|
def initialize(self, chat_handler): |
|
23
|
|
|
"""Store a reference to the "external" ChatHandler instance""" |
|
24
|
|
|
self.__ch = chat_handler |
|
25
|
|
|
|
|
26
|
|
|
def _get_current_user(self, param, callback): |
|
27
|
|
|
""" |
|
28
|
|
|
Check if the message really originates from Django. |
|
29
|
|
|
""" |
|
30
|
|
|
try: |
|
31
|
|
|
secret = self.get_query_argument("secret_key") |
|
32
|
|
|
if(secret == ""): |
|
33
|
|
|
#add user to the chat he is member of |
|
34
|
|
|
self._current_user = "Django" |
|
35
|
|
|
print("Right user.") |
|
36
|
|
|
print(self.request) |
|
37
|
|
|
print(self.request.arguments) |
|
38
|
|
|
print(self.get_argument("message", strip=False)) |
|
39
|
|
|
param = json.loads(self.get_argument("message", strip=False)) |
|
40
|
|
|
print(param) |
|
41
|
|
|
callback(param) |
|
42
|
|
|
print("Callback called.") |
|
43
|
|
|
self.set_status(200, "All right") |
|
44
|
|
|
print("get current user finished") |
|
45
|
|
|
return |
|
46
|
|
|
else: |
|
47
|
|
|
print("request not from Django") |
|
48
|
|
|
self._current_user = None |
|
49
|
|
|
self.set_status(403, "You are not authorised to access this server.") |
|
50
|
|
|
self.finish() |
|
51
|
|
|
except tornado.web.MissingArgumentError: |
|
52
|
|
|
print("request not from Django") |
|
53
|
|
|
self._current_user = None |
|
54
|
|
|
self.set_status(403, "You are not authorised to access this server.") |
|
55
|
|
|
self.finish() |
|
56
|
|
|
|
|
57
|
|
|
def get(self, token=None): |
|
58
|
|
|
""" |
|
59
|
|
|
Add a client to the list of authorized ones |
|
60
|
|
|
""" |
|
61
|
|
|
if not token: |
|
62
|
|
|
self.set_status(403, "You are not authorised to access this server.") |
|
63
|
|
|
# Get the current user. |
|
64
|
|
|
self._get_current_user(param=token, callback=self.__ch.add_potential_client) |
|
65
|
|
|
print("get finish.") |
|
66
|
|
|
|
|
67
|
|
|
def post(self, message=None): |
|
68
|
|
|
""" |
|
69
|
|
|
Post a message from Django |
|
70
|
|
|
""" |
|
71
|
|
|
print("Get method called") |
|
72
|
|
|
if not message: |
|
73
|
|
|
self.set_status(400, "Bad Request.") |
|
74
|
|
|
self.finish() |
|
75
|
|
|
print("Get Method successfully called") |
|
76
|
|
|
# Get the current user. |
|
77
|
|
|
self._get_current_user(param=message, callback=self.__ch.add_message) |
|
78
|
|
|
print("post finish.") |
|
79
|
|
|
self.set_status(200, "All right") |
|
80
|
|
|
print("post not finished yet") |
|
81
|
|
|
return |
|
82
|
|
|
|
|
83
|
|
|
def delete(self, token=None): |
|
84
|
|
|
""" |
|
85
|
|
|
Remove a client from the list of authorized ones |
|
86
|
|
|
""" |
|
87
|
|
|
if not token: |
|
88
|
|
|
self.set_status(403, "You are not authorised to access this server.") |
|
89
|
|
|
self.finish() |
|
90
|
|
|
# Get the current user. |
|
91
|
|
|
self._get_current_user(param=token, callback=self.__ch.remove_potential_client) |
|
92
|
|
|
self.finish() |
|
93
|
|
|
|
|
94
|
|
|
|
|
95
|
|
|
class ClientWSConnection(websocket.WebSocketHandler): |
|
96
|
|
|
|
|
97
|
|
|
def initialize(self, chat_handler): |
|
98
|
|
|
"""Store a reference to the "external" ChatHandler instance""" |
|
99
|
|
|
self.__ch = chat_handler |
|
100
|
|
|
|
|
101
|
|
|
def open(self, token): |
|
102
|
|
|
for user in self.__ch.authorized_tokens: |
|
103
|
|
|
if user.token == token.token and user.user.id == token.user_id: |
|
104
|
|
|
self.__ch.add_client_wsconn(user, self) |
|
105
|
|
|
print("WebSocket opened. ClientID = %s" % self.__token.user_id) |
|
106
|
|
|
break |
|
107
|
|
|
self.close(reason="You are not allowed to establish a connection with this server.", code=403) |
|
108
|
|
|
|
|
109
|
|
|
def on_message(self, message): |
|
110
|
|
|
print("Try to send a message the wrong way.") |
|
111
|
|
|
|
|
112
|
|
|
def on_close(self): |
|
113
|
|
|
print("WebSocket closed") |
|
114
|
|
|
self.__ch.remove_client(self.__token.user_id) |
|
115
|
|
|
|
|
116
|
|
|
""" |
|
117
|
|
|
Needs: |
|
118
|
|
|
- authorized_tokens = [{'token': token, 'user': user}] |
|
119
|
|
|
- add_potential_client({'token': token, 'user': user}) |
|
120
|
|
|
- add_client_wsconn(user, ClientWSConnection) |
|
121
|
|
|
- remove_client(user_id) |
|
122
|
|
|
- remove_potential_client({'token': token, 'user': user}) |
|
123
|
|
|
- add_message(message) |
|
124
|
|
|
- chatemate_cwsconns(user_id) |
|
125
|
|
|
- send_is_connected_msg(user_id) |
|
126
|
|
|
- send_is_disconnected_msg(user_id) |
|
127
|
|
|
""" |
|
128
|
|
|
class ChatHandler(object): |
|
129
|
|
|
"""Store data about connections, chats, which users are in which chats, etc.""" |
|
130
|
|
|
|
|
131
|
|
|
def __init__(self): |
|
132
|
|
|
self.authorized_tokens = [] # store the list of {'token': token, 'user': user} |
|
133
|
|
|
self.user_connections = {} # dict to store 'user_id': {'wsconn": wsconn, 'chats': [chat_id1, chat_id2]} |
|
134
|
|
|
self.chatmates = {} # store a set for each chat, each contains the id of the clients in the room. |
|
135
|
|
|
|
|
136
|
|
|
def add_potential_client(self, token): |
|
137
|
|
|
"""Add potential client to chat.""" |
|
138
|
|
|
self.authorized_tokens.append(token) |
|
139
|
|
|
|
|
140
|
|
|
def add_client_wsconn(self, user, conn): |
|
141
|
|
|
"""Store the websocket connection corresponding to an authorized client.""" |
|
142
|
|
|
self.user_connections[user.id]['wsconn'] = conn |
|
143
|
|
|
self.user_connections[user.id]['chats'] = [] |
|
144
|
|
|
|
|
145
|
|
|
# for each chat this user is member of, add the user to the chatmates of the chat |
|
146
|
|
|
for chatmember in user.user_chatmember: |
|
147
|
|
|
self.chatmates[chatmember.chat.id].add(user.id) |
|
148
|
|
|
self.user_connections[user.id]['chats'].append(chatmember.chat.id) |
|
149
|
|
|
|
|
150
|
|
|
# send "is_connected" messages |
|
151
|
|
|
self.send_is_connected_msg(user.id) |
|
152
|
|
|
|
|
153
|
|
|
def remove_client(self, user_id): |
|
154
|
|
|
"""Remove all client information from the chat handler.""" |
|
155
|
|
|
# first, remove the client connection from the corresponding chat in self.chatmates |
|
156
|
|
|
chats = self.user_connections[user_id] |
|
157
|
|
|
self.send_is_disconnected_msg(user_id) |
|
158
|
|
|
for chat_id in chats: |
|
159
|
|
|
self.chatmates[chat_id].discard(user_id) |
|
160
|
|
|
del self.user_connections[user_id] |
|
161
|
|
|
|
|
162
|
|
|
def remove_potential_client(self, token): |
|
163
|
|
|
"""Remove potential client from chat.""" |
|
164
|
|
|
del self.authorized_tokens[token] |
|
165
|
|
|
remove_client(token.user.id) |
|
166
|
|
|
|
|
167
|
|
|
def add_message(self, message): |
|
168
|
|
|
print('begin of the add_message method') |
|
169
|
|
|
print(message) |
|
170
|
|
|
print(message['chat']) |
|
171
|
|
|
print(message['chat']['id']) |
|
172
|
|
|
rconns = self.chatmate_cwsconns(message['chat']['id']) |
|
173
|
|
|
print(rconns) |
|
174
|
|
|
for conn in rconns: |
|
175
|
|
|
conn.write_message(message) |
|
176
|
|
|
print("Message sent") |
|
177
|
|
|
print("end of the sent messages"); |
|
178
|
|
|
|
|
179
|
|
|
def chatmate_cwsconns(self, chat_id): |
|
180
|
|
|
"""Return a list with the connections of the users currently connected to the specified chat.""" |
|
181
|
|
|
print('begin of the chatmate_cwsconns method.') |
|
182
|
|
|
print(self.chatmates) |
|
183
|
|
|
if chat_id in self.chatmates: |
|
184
|
|
|
print("found") |
|
185
|
|
|
return self.chatmates[chat_id] |
|
186
|
|
|
else: |
|
187
|
|
|
print('not found') |
|
188
|
|
|
return [] |
|
189
|
|
|
|
|
190
|
|
|
def send_is_connected_msg(self, user_id): |
|
191
|
|
|
"""Send a message of type 'is_connected' to all users connected to the chat where user_id is connected.""" |
|
192
|
|
|
r = set() |
|
193
|
|
|
#for every chat the user is part of |
|
194
|
|
|
for chat in self.user_connections[user_id]['chats']: |
|
195
|
|
|
#add the other members of the chat to the list of receivers |
|
196
|
|
|
r.update(self.chatmate_cwsconns(chat)) |
|
197
|
|
|
msg = {"msgtype": "is_connected", "username": user_id} |
|
198
|
|
|
pmessage = json.dumps(msg) |
|
199
|
|
|
for conn in r_cwsconns: |
|
200
|
|
|
conn.write_message(pmessage) |
|
201
|
|
|
|
|
202
|
|
|
def send_is_disconnected_msg(self, user_id): |
|
203
|
|
|
"""Send a message of type 'is_disconnected' to all users connected to the chat where user_id is disconnected.""" |
|
204
|
|
|
r = set() |
|
205
|
|
|
#for every chat the user is part of |
|
206
|
|
|
for chat in self.user_connections[user_id]['chats']: |
|
207
|
|
|
#add the other members of the chat to the list of receivers |
|
208
|
|
|
r.update(self.chatmate_cwsconns(chat)) |
|
209
|
|
|
msg = {"msgtype": "is_disconnected", "username": user_id} |
|
210
|
|
|
pmessage = json.dumps(msg) |
|
211
|
|
|
for conn in r_cwsconns: |
|
212
|
|
|
conn.write_message(pmessage) |
|
213
|
|
|
|