-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserver.py
140 lines (125 loc) · 5.04 KB
/
server.py
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
139
140
#!/usr/bin/env python3
"""a simple, websocket-based chatroom
This code was adapted in part from the websockets example:
https://websockets.readthedocs.io/en/stable/intro.html
"""
import asyncio
import json
import logging
import websockets
from collections import defaultdict
logging.basicConfig(level=logging.INFO)
# dictionary mapping socket to name
USERS = {} # websocket -> name
USER_ROOMS = {} # websocket -> room name
ROOM_USERS = defaultdict(set) # room -> set of websockets
async def notify_name_change(room, old, new):
"""Notify users that someone in [room] changed their name.
[room]: list of websockets
[old]: Old name. May be ""
[new]: New name.
"""
if old:
msg = json.dumps({"content": f"{old} changed their name to {new}."})
# treat identifying for the first time as "joining the room"
else:
msg = json.dumps({"content": f"{new} has entered the room."})
if room:
await asyncio.wait([asyncio.create_task(user.send(msg)) for user in room])
async def notify_join(room, name):
"""Notify users in [room] that someone [name] left.
[room]: list of websockets
[name]: name of the person joining
"""
# if the user never identified themselves, don't mention
# anything to anyone
if name:
msg = json.dumps({"content": f"{name} has entered the room."})
if room:
await asyncio.wait([asyncio.create_task(user.send(msg)) for user in room])
async def notify_leave(room, name):
"""Notify users in [room] that someone [name] left.
[room]: list of websockets
[name]: name of the person leaving
"""
# if the user never identified themselves, don't mention
# anything to anyone
if name:
msg = json.dumps({"content": f"{name} left the room."})
if room:
await asyncio.wait([asyncio.create_task(user.send(msg)) for user in room])
async def send_message(websocket, message):
"""handle [message] sent from [websocket]
[message] will be forwarded to other people in [websocket]'s room"""
if len(message) > 255:
await websocket.send(json.dumps({"content": "Please limit your message to 255 characters."}))
return
name = USERS[websocket]
room = ROOM_USERS[USER_ROOMS[websocket]]
# we don't allow the user to send a message if they
# have not yet identified themself
if name:
msg = json.dumps({"from": name, "content": message})
if room:
await asyncio.wait([asyncio.create_task(user.send(msg)) for user in room])
async def register(websocket):
logging.info(f"Registering: {websocket}")
USERS[websocket] = ""
await join_room(websocket, "Honda-Vehicles")
async def join_room(websocket, room_name):
USER_ROOMS[websocket] = room_name
ROOM_USERS[room_name].add(websocket)
await notify_join(ROOM_USERS[room_name], USERS[websocket])
async def leave_room(websocket):
room_name = USER_ROOMS[websocket]
ROOM_USERS[room_name].remove(websocket)
del USER_ROOMS[websocket]
await notify_leave(ROOM_USERS[room_name], USERS[websocket])
async def change_name(websocket, new_name):
logging.info(f"Changing name: {websocket}")
old = USERS[websocket]
USERS[websocket] = new_name
room = ROOM_USERS[USER_ROOMS[websocket]]
await notify_name_change(room, old, new_name)
async def change_room(websocket, new_room):
logging.info(f"Changing room: {websocket}")
await leave_room(websocket)
await websocket.send(
json.dumps({'content': f"(Moving to new room: {new_room})"})
)
await join_room(websocket, new_room)
async def unregister(websocket):
logging.info(f"Unregistering: {websocket}")
await leave_room(websocket)
del USERS[websocket]
async def counter(websocket, path):
# register(websocket) sends user_event() to websocket
await register(websocket)
try:
async for message in websocket:
logging.info(message)
try:
data = json.loads(message)
except json.decoder.JSONDecodeError as ex:
logging.error(ex)
continue
if data["kind"] == "name":
name = data["content"]
await change_name(websocket, name)
elif data["kind"] == "message":
msg = data["content"]
await send_message(websocket, msg)
elif data["kind"] == "room":
room = data["content"]
await change_room(websocket, room)
else:
logging.error("unsupported event: {}", data)
finally:
await unregister(websocket)
if __name__ == "__main__":
logging.info("Starting accord-chat, a simple chatroom server")
# note, you might need to change the hostname and port, depending
# on how you set this server up
start_server = websockets.serve(counter, "localhost", 6789)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()