import tkinter as tk
from socketio import Client


class ChatApp:
    def __init__(self, root):
        self.root = root
        self.sio = Client()
        self.setup_ui()
        self.setup_socket_events()

    def setup_ui(self):
        self.root.title("Chat Client")

        # Никнейм
        self.nick_frame = tk.Frame(self.root)
        self.nick_label = tk.Label(self.nick_frame, text="Ник:")
        self.nick_entry = tk.Entry(self.nick_frame)
        self.connect_btn = tk.Button(self.nick_frame, text="Подключиться", command=self.connect)
        self.nick_label.pack(side=tk.LEFT)
        self.nick_entry.pack(side=tk.LEFT, padx=5)
        self.connect_btn.pack(side=tk.LEFT)
        self.nick_frame.pack(pady=5)

        # Чат и пользователи
        self.main_frame = tk.Frame(self.root)
        self.chat_history = tk.Listbox(self.main_frame, width=50, height=20)
        self.user_list = tk.Listbox(self.main_frame, width=20, height=20)
        self.chat_history.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self.user_list.pack(side=tk.RIGHT, fill=tk.Y)
        self.main_frame.pack(pady=5)

        # Отправка сообщений
        self.msg_frame = tk.Frame(self.root)
        self.msg_entry = tk.Entry(self.msg_frame, width=50)
        self.send_btn = tk.Button(self.msg_frame, text="Отправить", command=self.send_message)
        self.msg_entry.pack(side=tk.LEFT)
        self.send_btn.pack(side=tk.LEFT, padx=5)
        self.msg_frame.pack(pady=5)

        self.connected = False

    def setup_socket_events(self):
        self.sio.on('connect', lambda: self.add_message("Соединение установлено"))
        self.sio.on('disconnect', lambda: self.add_message("Соединение разорвано"))
        self.sio.on('registration_success', self.on_registration_success)
        self.sio.on('registration_failed', lambda: self.add_message("Ник занят!"))
        self.sio.on('update_users', self.update_user_list)
        self.sio.on('message', self.handle_message)

    def connect(self):
        if not self.connected:
            nickname = self.nick_entry.get()
            if nickname:
                try:
                    self.sio.connect('http://localhost:5000')
                    self.sio.emit('register', nickname)
                except Exception as e:
                    self.add_message(f"Ошибка: {str(e)}")
        else:
            self.sio.disconnect()
            self.connected = False
            self.connect_btn.config(text="Подключиться")
            self.nick_entry.config(state=tk.NORMAL)

    def on_registration_success(self):
        self.connected = True
        self.connect_btn.config(text="Отключиться")
        self.nick_entry.config(state=tk.DISABLED)
        self.add_message("Вы успешно подключились!")

    def update_user_list(self, users):
        self.user_list.delete(0, tk.END)
        for user in users:
            self.user_list.insert(tk.END, user)

    def handle_message(self, data):
        self.add_message(f"{data['sender']}: {data['message']}")

    def send_message(self):
        if self.connected and self.msg_entry.get():
            receiver = self.get_selected_user() or 'all'
            self.sio.emit('message', {
                'to': receiver,
                'message': self.msg_entry.get(),
                'sender': self.nick_entry.get()
            })
            self.msg_entry.delete(0, tk.END)

    def get_selected_user(self):
        try:
            return self.user_list.get(self.user_list.curselection())
        except:
            return None

    def add_message(self, text):
        self.chat_history.insert(tk.END, text)
        self.chat_history.yview(tk.END)


if __name__ == '__main__':
    root = tk.Tk()
    app = ChatApp(root)
    root.mainloop()

Сделай улучшение интерфейса и дизайна. Можно использовать анимацию и доработать функционал. Должно быть похоже на телеграм