0

I am creating an application where students can communicate with tutors, I however can't seem to figure out how to create an effective socket between the 2 clients (a tutor and a student). Most socket tutorials use a single file for the clients. I am new to WebSockets, kindly assist. Here is my FastAPI backend API endpoint

connected_clients = {}


def generate_room_id(sender_username, recipient_username):
    return '_'.join(sorted([sender_username, recipient_username]))


@router.websocket("/ws/")
async def chat_websocket(websocket: WebSocket, db: Session = Depends(get_db)):
    await websocket.accept()
    recipientUsername = None
    try:
        while True:
            data = await websocket.receive_text()
            message_data = json.loads(data)
            recipientUsername = message_data["recipientUsername"]
            content = message_data["content"]
            senderUsername = message_data["senderUsername"]
            room_id = generate_room_id(senderUsername, recipientUsername)
            connected_clients.setdefault(room_id, []).append(websocket)
            timestamp = datetime.utcnow()
            user = db.query(User).filter(
                User.username == recipientUsername).first()
            message = Message(user_id=user.id,
                              content=content,
                              timestamp=timestamp)
            db.add(message)
            db.commit()

            # Broadcast the message to all users in the same room
            await broadcast_message(room_id, data, websocket)

            print(connected_clients)

    except WebSocketDisconnect:
        if recipientUsername in connected_clients:
            del connected_clients[recipientUsername]
        await websocket.close()


async def broadcast_message(
    room_id: str,
    message_data: str,
        sender_websocket: WebSocket):
    recipient_websockets = connected_clients.get(room_id, [])
    for recipient_websocket in recipient_websockets:
        try:
            # Skip broadcasting to the sender's WebSocket
            if recipient_websocket != sender_websocket:
                await recipient_websocket.send_text(message_data)
        except WebSocketDisconnect:
            # Handle recipient disconnection
            connected_clients[room_id].remove(recipient_websocket)

Here is my student.html JavaScript

const scrollToBottom = () => {
            const chatContainer = document.getElementById('scrollContainer');
            chatContainer.scrollTop = chatContainer.scrollHeight;
        };

        function createChatMessage({senderUsername, content}, isSender) {
            const messageContainer = document.createElement('div');
            messageContainer.classList.add('chat__message');
            messageContainer.classList.add('chat__message_type_operator');
            messageContainer.classList.add('js-msg-cont');
            messageContainer.style.display = 'flex';
            if(isSender){
                messageContainer.style.flexDirection = 'row-reverse';
            }else{
                messageContainer.style.flexDirection = 'row';
                messageContainer.classList.add('chat__message_type_user');
            }
            messageContainer.setAttribute('data-is-new', '0');
            messageContainer.setAttribute('data-msg-id', '5766');

            const avatarContainer = document.createElement('div');
            avatarContainer.classList.add('answer__avatar', 'chat__avatar');
            avatarContainer.classList.add('chat__avatar_type_operator');
                //avatarContainer.classList.add('chat__avatar_type_user');

            const avatarImage = document.createElement('img');
            avatarImage.src = `/static/images/${1}.png`; // Replace with appropriate image URL
            avatarImage.alt = senderUsername;
            avatarImage.width = 32;
            avatarImage.height = 32;
            avatarImage.classList.add("mCS_img_loaded")
            avatarContainer.appendChild(avatarImage);

            const textContainer = document.createElement('div');
            textContainer.classList.add('chat__text');
            //textContainer.classList.add('chat__text_type_operator');
            textContainer.classList.add('chat__text_type_user');

            const messageParagraph = document.createElement('p');
            messageParagraph.textContent = content;

            const timeElement = document.createElement('time');
            timeElement.classList.add('chat__time');
            timeElement.textContent = "3:13am";

            textContainer.appendChild(messageParagraph);
            textContainer.appendChild(timeElement);

            messageContainer.appendChild(avatarContainer);
            messageContainer.appendChild(textContainer);

            return messageContainer;
        }

        // JavaScript code to handle form submission using WebSocket
        const form = document.querySelector('#message-form-{{ tutor.username }}');
        const chatArea = document.querySelector('.chat-area-{{ tutor.username }}');
        const socketURL = `ws://${window.location.host}/ws/`;
        const formData = new FormData(form);
        const recipientUsername = formData.get('recipientUsername');
        const senderUsername = formData.get("senderUsername");
        const msgContent = document.getElementById("messageform-body");
        let content;
        const task_id = form.getAttribute('data-task-id');
        const bid_id = form.getAttribute('data-bid-id');
        const queryParams = `task_id=${task_id}&bid_id=${bid_id}&username=${recipientUsername}`;
        const wsURL = `${socketURL}?${queryParams}`;
        const socket = new WebSocket(wsURL);

        const sendMessage = () => {
            content = msgContent.value
            const message = { recipientUsername, senderUsername, content };
            const messageData = JSON.stringify(message);
            socket.send(messageData);
            chatArea.appendChild(createChatMessage(message, true));
            scrollToBottom();
        }

        socket.onopen = () => {
            console.log("Websocket connected");
        };

        socket.onmessage = event => {
            console.log("Message received:", event.data);
            const message = JSON.parse(event.data);
            chatArea.appendChild(createChatMessage(message, false));
            scrollToBottom();
        };

        form.addEventListener("submit", (event) => {
            event.preventDefault();
            sendMessage();
            form.reset();
        })

        socket.onclose = () => console.log("WebSocket connection closed.");

And here is my tutor.html JavaScript

const scrollToBottom = () => {
                            const chatContainer = document.getElementById('scrollContainer');
                            chatContainer.scrollTop = chatContainer.scrollHeight;
                          };

                          function createChatMessage({senderUsername, content}, isSender) {
                            const messageContainer = document.createElement('div');
                            messageContainer.classList.add('chat__message', 'js-msg-cont');
                            messageContainer.classList.add('chat__message_type_operator');
                            if(isSender){
                              messageContainer.style.flexDirection = 'row-reverse';
                          }else{
                              messageContainer.style.flexDirection = 'row';
                              messageContainer.classList.add('chat__message_type_user');
                          }
                            //messageContainer.style.flexDirection = 'row-reverse';
                            //messageContainer.classList.add('chat__message_type_user');
                            messageContainer.setAttribute('data-is-new', '0');
                            messageContainer.setAttribute('data-msg-id', '1714757');

                            const avatarContainer = document.createElement('div');
                            avatarContainer.classList.add('answer__avatar', 'chat__avatar');
                            avatarContainer.classList.add('chat__avatar_type_operator');
                            //avatarContainer.classList.add('chat__avatar_type_user');
                            const avatarImage = document.createElement('img');
                            avatarImage.src = `/static/images/${1}.png`; // Replace with appropriate image URL
                            avatarImage.alt = senderUsername;
                            avatarImage.width = 32;
                            avatarImage.height = 32;
                            avatarContainer.appendChild(avatarImage);

                            const textContainer = document.createElement('div');
                            textContainer.classList.add('chat__text');
                            //textContainer.classList.add('chat__text_type_operator');
                            textContainer.classList.add('chat__text_type_user');
                            textContainer.id = `chat-area-${senderUsername}`;

                            const messageParagraph = document.createElement('p');
                            messageParagraph.textContent = content;

                            const timeElement = document.createElement('time');
                            timeElement.classList.add('chat__time');
                            timeElement.textContent = "3:13am";

                            const readSpan = document.createElement('span');
                            readSpan.classList.add('js-read-msg-mark-1714757');
                            readSpan.textContent = ', Read';

                            timeElement.appendChild(readSpan);
                            textContainer.appendChild(messageParagraph);
                            textContainer.appendChild(timeElement);

                            messageContainer.appendChild(avatarContainer);
                            messageContainer.appendChild(textContainer);

                            return messageContainer;
                        }
                          const socketURL = `ws://${window.location.host}/ws/`;
                          const form = document.querySelector('.js-chat-form');
                          const button = form.querySelector('#submit');
                          const chatArea = document.querySelector('.chat-area-{{student.username}}');
                          const formData = new FormData(form);
                          const recipientUsername = formData.get('recipientUsername');
                          const senderUsername = formData.get("senderUsername");
                          const task_id = form.getAttribute('data-task-id');
                          const bid_id = form.getAttribute('data-bid-id');
                          const queryParams = `task_id=${task_id}&bid_id=${bid_id}&recipientUsername=${recipientUsername}`;
                          const wsURL = `${socketURL}?${queryParams}`;
                          const socket = new WebSocket(wsURL);
                          const msgContent = document.getElementById("messageform-body");
                          let content;

                          const sendMessage = () => {
                            content = msgContent.value
                            message = { recipientUsername, senderUsername, content }
                            const messageData = JSON.stringify(message);
                            socket.send(messageData);
                            chatArea.appendChild(createChatMessage(message, true));
                            scrollToBottom();
                          }

                          socket.onopen = () => {
                            console.log("Websocket connected");
                          };

                          socket.onmessage = event => {
                            console.log("Message received:", event.data);
                            const message = JSON.parse(event.data);
                            chatArea.appendChild(createChatMessage(message, false));
                            scrollToBottom();
                          };

                          form.addEventListener("submit", (event) => {
                            event.preventDefault();
                            sendMessage();
                            form.reset();
                          })

                          socket.onclose = () => console.log("WebSocket connection closed.");

The connection is working ok but the second client cannot receive the first client's message until it has also sent a message. That is the onmessage event on both JavaScripts isn't being executed until the message is send by the second client. Kindly help in debugging this.

Vik
  • 41
  • 8

0 Answers0