import socket
import threading
clients = {}
lock = threading.Lock()
colors = ["\033[92m", "\033[94m", "\033[93m", "\033[95m", "\033[96m", "\033[91m"]
color_idx = 0
def broadcast(msg, sender=None):
with lock:
for c in clients:
if c != sender:
try:
c.send(msg.encode())
except:
del clients[c]
c.close()
def handle(conn, addr):
global color_idx
try:
nick = conn.recv(1024).decode().strip()
if not nick:
nick = f"User_{addr[1]}"
except:
conn.close()
return
with lock:
color = colors[color_idx % len(colors)]
color_idx += 1
clients[conn] = {"nick": nick, "color": color, "addr": addr}
join_msg = f"{color}[Server] {nick} joined the chat\033[0m"
print(f"[+] {nick} ({addr}) connected")
broadcast(join_msg, conn)
try:
while True:
data = conn.recv(1024).decode()
if not data:
break
if data.startswith("/list"):
with lock:
online = ", ".join([c["nick"] for c in clients.values()])
conn.send(f"\033[93mOnline ({len(clients)}): {online}\033[0m".encode())
continue
if data.startswith("@"):
parts = data.split(" ", 1)
target_nick = parts[0][1:]
msg_text = parts[1] if len(parts) > 1 else ""
with lock:
target_conn = next((c for c, info in clients.items() if info["nick"] == target_nick), None)
if target_conn:
sender_info = clients[conn]
target_conn.send(f"{sender_info['color']}[PM from {sender_info['nick']}] {msg_text}\033[0m".encode())
else:
conn.send(f"\033[91mUser {target_nick} not found\033[0m".encode())
continue
sender_info = clients[conn]
msg = f"{sender_info['color']}[{sender_info['nick']}] {data}\033[0m"
print(f"[{nick}] {data}")
broadcast(msg, conn)
except:
pass
with lock:
if conn in clients:
info = clients.pop(conn)
print(f"[-] {info['nick']} ({addr}) disconnected")
broadcast(f"\033[90m[Server] {info['nick']} left the chat\033[0m", conn)
conn.close()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("0.0.0.0", 5555))
s.listen()
print("Server started on port 5555")
while True:
conn, addr = s.accept()
threading.Thread(target=handle, args=(conn, addr), daemon=True).start()