forked from Erkenbend/tcp-chat-room
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.py
More file actions
159 lines (134 loc) · 5.94 KB
/
Copy pathserver.py
File metadata and controls
159 lines (134 loc) · 5.94 KB
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import threading
import socket
ENCODING = 'utf-8'
SERVER_ADDRESS = ('127.0.0.1', 15662)
AUTHORIZED_ADMIN_CREDENTIALS = ['admin:adminpass', 'joe:secure']
class Participant:
def __init__(self, nickname: str, client_socket: socket, is_admin=False):
self.nickname = nickname
self.client_socket = client_socket
self.is_admin = is_admin
def broadcast(message: str, ignore_list=None, admin_only=False):
if ignore_list is None:
ignore_list = []
for participant in participants:
if participant not in ignore_list:
if not admin_only or participant.is_admin:
participant.client_socket.send(message.encode(ENCODING))
def send_to(participant: Participant, message: str):
return participant.client_socket.send(message.encode(ENCODING))
def receive_from(participant: Participant, size: int = 1024):
return participant.client_socket.recv(size).decode(ENCODING)
def kick_user(nickname: str):
try:
participant_idx = list(map(lambda p: p.nickname, participants)).index(nickname)
except ValueError:
print(f'Could not kick {nickname} -- user not found')
return False
participant_to_kick = participants[participant_idx]
participants.remove(participant_to_kick)
send_to(participant_to_kick, '-- You have been kicked by an admin')
participant_to_kick.client_socket.close()
print(f'Kicked {nickname}')
broadcast(f'-- {nickname} has been kicked')
return True
def handle_command(participant, message):
print(f'Command by {participant.nickname}: "{message}"')
command_message: str = message.strip()
command_name = command_message.split(' ', 1)[0]
# non-admin commands
if command_name == '/admin':
send_to(participant, '%PASS%')
password = receive_from(participant)
if f'{participant.nickname}:{password}' in AUTHORIZED_ADMIN_CREDENTIALS:
participant.is_admin = True
print(f'{participant.nickname} is now an admin')
send_to(participant, '-- You are now an admin')
broadcast(f'-- User {participant.nickname} is now an admin', [participant], True)
else:
send_to(participant, '-- Invalid password')
elif command_name == '/quit' or command_name == '/exit':
send_to(participant, '%QUIT%')
participant.client_socket.close()
elif command_name == '/online':
send_to(participant, f'-- Currently online: {list(map(lambda p: p.nickname, participants))}')
# admin commands
elif not participant.is_admin:
send_to(participant, '-- Admin privileges required')
else:
if command_name == '/kick':
nickname_to_kick = command_message[6:]
if kick_user(nickname_to_kick):
broadcast(f'-- User {nickname_to_kick} kicked by {participant.nickname}', None, True)
else:
send_to(participant, f'-- Could not kick {nickname_to_kick}')
elif command_name == '/ban':
nickname_to_ban = command_message[5:]
if kick_user(nickname_to_ban):
banned_nicknames.append(nickname_to_ban)
broadcast(f'-- User {nickname_to_ban} kicked and banned by {participant.nickname}', None, True)
else:
send_to(participant, f'-- Could not kick {nickname_to_ban}')
elif command_name == '/unban':
nickname_to_unban = command_message[7:]
try:
banned_nicknames.remove(nickname_to_unban)
broadcast(f'-- User {nickname_to_unban} unbanned by {participant.nickname}', None, True)
except ValueError:
send_to(participant, f'-- User {nickname_to_unban} was not in ban list')
elif command_name == '/banned':
send_to(participant, f'-- Currently banned: {banned_nicknames}')
else:
send_to(participant, '-- Invalid command')
def handle(participant: Participant):
"""
Handle each new input from participant;
this method runs in a separate thread for each participant
"""
while True:
try:
message = receive_from(participant)
if message.startswith('/'):
handle_command(participant, message)
else:
broadcast(f'{participant.nickname}: {message}', [participant])
except Exception as e:
print(e)
try:
participants.remove(participant)
participant.client_socket.close()
broadcast(f'-- {participant.nickname} left the chat')
finally:
break
def receive():
"""
Main loop, add new clients to the chat room
"""
while True:
client_socket, address = server.accept()
print(f'Connected with {str(address)}')
client_socket.send('%NICK%'.encode(ENCODING))
nickname = client_socket.recv(1024).decode(ENCODING)
if nickname in banned_nicknames:
print(f'Connection attempt with banned nickname: "{nickname}"')
client_socket.send('%BANNED%'.encode(ENCODING))
else:
if nickname in list(map(lambda p: p.nickname, participants)):
print(f'Connection attempt with existing nickname: "{nickname}"')
client_socket.send('%DUPLICATE%'.encode(ENCODING))
else:
new_participant = Participant(nickname, client_socket)
participants.append(new_participant)
print(f'New participant: {nickname}')
broadcast(f'-- User {nickname} joined!', [new_participant])
send_to(new_participant, '-- Connected to server!')
thread = threading.Thread(target=handle, args=(new_participant,))
thread.start()
if __name__ == '__main__':
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(SERVER_ADDRESS)
server.listen()
participants = []
banned_nicknames = []
print('Server is listening... ')
receive()