diff --git a/server.py b/server.py new file mode 100644 index 0000000..119e195 --- /dev/null +++ b/server.py @@ -0,0 +1,167 @@ +import socket +import threading +import json +import logging +from pathlib import Path + +logging.basicConfig( + filename="server.log", + level=logging.INFO, + format="%(asctime)s [%(levelname)s] %(message)s", +) + +def log(msg): + print(msg) + logging.info(msg) + +def get_local_ip(): + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + try: + s.connect(("8.8.8.8", 80)) + return s.getsockname()[0] + except: + return "127.0.0.1" + finally: + s.close() + +local_ip = get_local_ip() + +server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + +host = "0.0.0.0" +port = 44703 + +server.bind((host, port)) +server.listen() + +actual_port = server.getsockname()[1] + +log(f"[SERVER] LAN IP: {local_ip}") +log(f"[SERVER] Server live at {local_ip}:{actual_port}") + +chats_file = Path("chats2.json") + +if not chats_file.exists(): + with open(chats_file, "w") as f: + json.dump({"general": {"users": [], "messages": []}}, f, indent=4) + +lock = threading.Lock() +clients_lock = threading.Lock() + +def load_chats(): + with lock: + try: + with chats_file.open("r", encoding="utf-8") as f: + return json.load(f) + except: + return {"general": {"users": [], "messages": []}} + +def save_chats(data): + with lock: + with chats_file.open("w", encoding="utf-8") as f: + json.dump(data, f, indent=4) + +clients = [] +chat_clients = {} + +def handle_client(client_socket, addr): + with clients_lock: + clients.append(client_socket) + + log(f"[SERVER] Client connected: {addr}") + + username = None + + try: + username = client_socket.recv(1024).decode("utf-8").strip() + log(f"[SERVER] User connected: {username}") + + chats = load_chats() + client_socket.sendall(json.dumps(chats).encode("utf-8")) + + while True: + data = client_socket.recv(4096) + if not data: + break + + try: + request = json.loads(data.decode("utf-8")) + except json.JSONDecodeError: + continue + + action = request.get("action") + chats = load_chats() + + if action == "new_chat": + chat_name = request["chat_name"] + users = request.get("users", [username]) + + if chat_name not in chats: + chats[chat_name] = {"users": users, "messages": []} + save_chats(chats) + log(f"[SERVER] New chat created: {chat_name}") + + client_socket.sendall(json.dumps(chats).encode("utf-8")) + + elif action == "message": + chat_name = request["chat_name"] + message = request["message"] + + if chat_name in chats: + chats[chat_name]["messages"].append({ + "user": username, + "message": message + }) + + save_chats(chats) + log(f"[SERVER] {username} in {chat_name}: {message}") + + with clients_lock: + for client in clients[:]: + try: + client.sendall(json.dumps(chats).encode("utf-8")) + except: + try: + client.close() + except: + pass + clients.remove(client) + + elif action == "add_user": + chat_name = request["chat"] + new_user = request["user"] + + if chat_name in chats: + if new_user not in chats[chat_name]["users"]: + chats[chat_name]["users"].append(new_user) + save_chats(chats) + log(f"[SERVER] Added {new_user} to {chat_name}") + + client_socket.sendall(json.dumps(chats).encode("utf-8")) + + elif action == "openchat": + chat_name = request["chat_name"] + if chat_name in chats: + client_socket.sendall(json.dumps(chats).encode("utf-8")) + else: + client_socket.sendall(json.dumps(chats).encode("utf-8")) + + except Exception as e: + log(f"[ERROR] {e}") + + finally: + with clients_lock: + if client_socket in clients: + clients.remove(client_socket) + + client_socket.close() + log(f"[SERVER] Client disconnected: {addr}") + +while True: + client_sock, address = server.accept() + threading.Thread( + target=handle_client, + args=(client_sock, address), + daemon=True + ).start()