From 6ff5954f3ae236c6ee53c701c083e096b8d9dee6 Mon Sep 17 00:00:00 2001 From: Ben de Roo Date: Tue, 17 Feb 2026 10:30:28 +0100 Subject: [PATCH] Add client.py --- client.py | 231 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 client.py diff --git a/client.py b/client.py new file mode 100644 index 0000000..444183b --- /dev/null +++ b/client.py @@ -0,0 +1,231 @@ +import socket +import os +import shutil +import hashlib +import json +import getpass +from pathlib import Path +import threading +import readline + +wit = "\x1b[97m" +blauw_bg = "\x1b[44m" +groen = "\x1b[92m" +rood = "\x1b[91m" +reset = "\x1b[0m" + +commands = ["/add", "/quit", "/break", "/users"] +chat_users = [] +chat_messages = [] +lock = threading.Lock() + +def completer(text, state): + buffer = readline.get_line_buffer() + parts = buffer.split() + + if not buffer.startswith("/"): + return None + + if len(parts) == 1: + matches = [cmd for cmd in commands if cmd.startswith(parts[0])] + if state < len(matches): + return matches[state] + return None + + if parts[0] == "/add" and len(parts) >= 2: + current_input = parts[-1] + matches = [user for user in chat_users if user.startswith(current_input)] + if state < len(matches): + return matches[state] + return None + + return None + +readline.set_completer_delims(" \t\n") +readline.set_completer(completer) +readline.parse_and_bind("tab: complete") + +def clear(): + os.system("cls" if os.name == "nt" else "clear") + +def term_size(): + size = shutil.get_terminal_size() + return size.columns, size.lines + +def print_white(text=""): + print(wit + text + reset) + +def login(): + base_dir = Path(__file__).parent + passwd_file = base_dir / "passwd.json" + if not passwd_file.exists(): + return None + try: + with passwd_file.open("r", encoding="utf-8") as f: + data = json.load(f) + except: + data = {} + user = input(wit + "Gebruikersnaam: " + reset) + password = getpass.getpass(wit + "Wachtwoord: " + reset) + password_hash = hashlib.sha256(password.encode("utf-8")).hexdigest() + if user in data and data[user] == password_hash: + return user + return None + +def signup(): + base_dir = Path(__file__).parent + passwd_file = base_dir / "passwd.json" + if passwd_file.exists(): + try: + with passwd_file.open("r", encoding="utf-8") as f: + data = json.load(f) + except: + data = {} + else: + data = {} + user = input(wit + "Gebruiker: " + reset) + if user in data: + return + password = getpass.getpass(wit + "Wachtwoord: " + reset) + data[user] = hashlib.sha256(password.encode("utf-8")).hexdigest() + with passwd_file.open("w", encoding="utf-8") as f: + json.dump(data, f, indent=4) + +current_user = None + +while not current_user: + clear() + w, _ = term_size() + print(blauw_bg + wit + "Terminal Chat ".center(w) + reset) + print_white("1. inloggen") + print_white("2. nieuw account") + keuze = input(wit + " > " + reset) + if keuze == "1": + current_user = login() + elif keuze == "2": + signup() + +host = "192.168.254.36" +port = 44703 +try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect((host, port)) + s.sendall(current_user.encode("utf-8")) +except Exception as e: + print("Connection failed:", e) + exit() + +data = s.recv(4096) +chat_data = json.loads(data.decode("utf-8")) +chat_names = list(chat_data.keys()) + +def draw_chat_window(messages, chatname): + clear() + w, h = term_size() + print(blauw_bg + wit + chatname.center(w) + reset) + available_lines = h - 3 + to_display = messages[-available_lines:] + for msg in to_display: + user = msg["user"] + text = msg["message"] + if user == current_user: + print(wit + f" {text} [you]".rjust(w) + reset) + else: + print_white(f"[{user}] {text}") + print("-" * w) + +def chat_session(chatkeuze): + global chat_messages + global chat_users + + s.sendall(json.dumps({"action": "openchat", "chat_name": chatkeuze}).encode("utf-8")) + data = s.recv(4096) + chat_data = json.loads(data.decode("utf-8")) + + if chatkeuze in chat_data: + chat_messages = chat_data[chatkeuze].get("messages", []) + chat_users = chat_data[chatkeuze].get("users", []) + + def receive_messages(sock): + global chat_messages + global chat_users + + while True: + try: + data = sock.recv(4096) + if not data: + break + + decoded = json.loads(data.decode("utf-8")) + + # /users response + if isinstance(decoded, dict) and decoded.get("type") == "users_list": + users = decoded.get("users", []) + print("\nUsers:", ", ".join(users)) + print(wit + "Typ je bericht: " + reset, end="", flush=True) + continue + + # normale chat update + if isinstance(decoded, dict) and chatkeuze in decoded: + with lock: + chat_messages = decoded[chatkeuze].get("messages", []) + chat_users = decoded[chatkeuze].get("users", []) + + draw_chat_window(chat_messages, chatkeuze) + print(wit + "Typ je bericht: " + reset, end="", flush=True) + + except Exception: + break + + threading.Thread(target=receive_messages, args=(s,), daemon=True).start() + + while True: + draw_chat_window(chat_messages, chatkeuze) + bericht = input(wit + "Typ je bericht: " + reset) + + if bericht.lower() == "/quit": + exit() + elif bericht.lower() == "/break": + break + elif bericht.lower() == "/users": + s.sendall(json.dumps({ + "action": "get_users", + "chat": chatkeuze + }).encode("utf-8")) + elif bericht.lower().startswith("/add "): + target_user = bericht.split(" ", 1)[1] + s.sendall(json.dumps({ + "action": "add_user", + "chat": chatkeuze, + "user": target_user, + "by": current_user + }).encode("utf-8")) + else: + with lock: + chat_messages.append({ + "user": current_user, + "message": bericht + }) + draw_chat_window(chat_messages, chatkeuze) + s.sendall(json.dumps({ + "action": "message", + "chat_name": chatkeuze, + "message": bericht + }).encode("utf-8")) + +while True: + print_white("Beschikbare chats:") + for i, chat in enumerate(chat_names, start=1): + print_white(f"{i}. {chat}") + print_white(f"{len(chat_names)+1}. Nieuwe chat aanmaken") + keuze = input(wit + " > " + reset) + + if keuze.isdigit() and int(keuze) == len(chat_names)+1: + nieuwe_chat = input("Naam van nieuwe chat: ") + s.sendall(json.dumps({"action": "new_chat", "chat_name": nieuwe_chat}).encode("utf-8")) + data = s.recv(4096) + chat_data = json.loads(data.decode("utf-8")) + chat_names = list(chat_data.keys()) + elif keuze.isdigit() and 1 <= int(keuze) <= len(chat_names): + chatkeuze = chat_names[int(keuze)-1] + chat_session(chatkeuze)