-1

I'm building a simple chat application with Flask and SocketIO. The user enters a username and a room key to join a room and start chatting.

In the beginning, I could make the sockets work to send and receive messages. However, at that moment I was not using any blueprint. Just templates and all the events and routes were in the same python file.

Later, I decided to split the application into folders and use blueprints. Mainly due to the incorporation of registration and authentication of users (No incorporated yet). However, the sockets stopped working but everything else is working as expected.

The directory tree looks as follows:

Chat_Rooms/
|
--- flask_chat_app/
|   |
|   ---  static/css/
|   ---  templates/
|
|   ---  __init__.py
|   ---  auth.py
|   ---  events.py
|   ---  models.py
|   ---  views.py
|
--- app.py

My python and HTML files look as follows:

_init_.py

from flask import Flask
from flask_socketio import SocketIO

# create socket object
socket:SocketIO = SocketIO()

def create_app(debug=False):

    """Create application"""
    app:Flask = Flask(__name__)
    app.debug = debug
    app.config['SECRET_KEY'] = 'mysecret!'

    """import and add blueprints to app"""
    from .auth import auth
    from .views import views

    app.register_blueprint(views, url_prefix="/")
    app.register_blueprint(auth, url_prefix="/")
    
    """Initialize socket"""
    socket.init_app(app)

    """Return built app"""
    return app

views.py

from flask import Blueprint
from flask import render_template, request
import random

# init blueprint object
views:Blueprint = Blueprint('views', __name__)

# Routes
"""
    index
        First page the user will see when enter to site
"""
@views.route("/")
def index():
    return render_template("index.html")

"""
    home:
        Display options to generate key and join room
"""
@views.route("/home")
def home():
    return render_template("home.html")

"""
    generateKey:
        Generate 2 random number between 100 - 1000. Put them together
        to create a random key shown in the frontend.

        Key is saved into a session as room-key
        Increase room counter
"""
@views.route("/generate_key")
def generateKey():

    # generate a random key
    key1:int = random.randint(100,1000)
    key2:int = random.randint(100,1000)
    key:str = str(key1) + ' - ' + str(key2)

    # render template with key
    return render_template("generate_key.html", key=key)

"""
    join_room_credentials:
        Get username and room key. If it exist, enter room chat
"""
@views.route("/join_room", methods=['GET', 'POST'])
def join_room_credentials():
    
    # get user information and enter chat room
    if request.method == 'POST':
        if request.form["username"] and request.form["roomKey"]:

            # redirect to chat room page
            return render_template("chat_room.html", username=request.form["username"], room=request.form["roomKey"])

    # render html file to get credentials
    else:
        return render_template("join_room.html")

events.py

from .__init__ import socket
from flask_socketio import join_room

# socket routes
"""
    handle_join_room:
        event that will get the users in the same room and will notify to all clients
        when a new user joins a room
"""
@socket.on("user_join_room")
def handle_join_room(data):

    # print user and room in terminal
    print("\n")
    print(f"{data['username']} joined to room{data['room']}")
    print("\n")

    # add user to group
    join_room(data["room"])

    # send notification to clients
    socket.emit('user_join_room', f"{data['username']} joined the room", to=data["room"])

"""
    handle_message:
        event to send a client message to all other clients
"""
@socket.on("user_message")
def handle_message(message):
    socket.emit("user_message", f". {message['username']}: {message['message']} .", to=message["room"])

chat_room.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Chat Room</title>
        <link rel="stylesheet" href="{{url_for("static", filename="CSS/chat_room.css")}}">
        
        <!-- connect to server -->
        <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>

        <script>
            // create socket to connect with server
            const socket = io.connect("http://127.0.0.1:5000/")

            /* --------- client to server ----------- */

            // send message to server with user credentials
            socket.on("connect", function() {

                socket.emit('user_join_room', {
                    username: "{{username}}",
                    room: "{{room}}"
                })

                // get message text 
                let msgElem = document.getElementById("msg_input");

                let msgForm = document.getElementById("mgs-form");
                msgForm.onsubmit = function (event) {
                    event.preventDefault();

                    let msg = msgElem.value.trim();
                    if (msg.length) {
                        socket.emit("user_message", {
                            username: "{{username}}",
                            room: "{{room}}",
                            message: msg
                        })
                    }

                    msgElem.value = " ";
                    msgElem.focus();
                }
            });

            /* ---------- server to client ------------ */

            // add message when someone joins the room
            socket.on("user_join_room", function(data) {
                
                // create new paragraph
                let newP = document.createElement("p");
                let newNode = document.createTextNode(data);
                newP.appendChild(newNode);

                // get message div element
                const msgDiv = document.getElementById("message");
                msgDiv.appendChild(newP);

            })

            socket.on("user_message", function (data) {
                let newP = document.createElement("p");
                let newNode = document.createTextNode(" " + data + " ");
                newP.appendChild(newNode);

                // get message div element
                const msgDiv = document.getElementById("message");
                msgDiv.appendChild(newP);

            })

        </script>

    </head>

    <body>
        
        <div id="chat-wrap">

            <div id="header-wrap">
                <h1>Room: {{room}}</h1>
            </div>

            <div id="chat-space">
                <p id="message"></p>
            </div>

            <div id="msg-wrap">
                <form id="mgs-form">
                    <input id="msg_input" placeholder="Enter message"></input>
                    <button type="submit">Send</button>
                </form>
            </div>

        </div>

    </body>
</html>

join_room.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Join Room</title>

        <link rel="stylesheet" href="{{url_for("static", filename="CSS/join_room.css")}}">
    </head>

    <body>
        <!-- Get username and room key -->
        <div id="credential-wrap">
            <form method="POST">

                <div id="username_wrapper">
                    <label>Username: </label>
                    <input name="username" placeholder="frazzledcoal"></input>
                </div>
    
                <div id="key_wrapper">
                    <label>Room Key: </label>
                    <input name="roomKey"  placeholder="xxx - xxx"></input>
                </div>
    
                <div id="button-wrap">
                    <a href="{{url_for("views.home")}}"><button type="button">Back</button></a>
                    <button type="submit" id="submit-btn">Enter</button>                    
                </div>
            </form>    
        </div>

    </body>
</html>

app.py

from flask_chat_app import create_app, socket

"""build application"""
chat_app = create_app(True)

if __name__ == "__main__":
    socket.run(chat_app)

I tried to find a solution by looking at the Flask and SocketIO documentation but I still don't know why the sockets stopped communicating with each other.

I really would appreciate it if you guys can help me to solve this problem. This is my first stack overflow post, so I do apologize if any information is missing.

Javs
  • 1

1 Answers1

0

You have to import events.py somewhere for those event handlers to be seen by Flask-SocketIO. For example, in your create_app function:

    """import and add blueprints and events to app"""
    from .auth import auth
    from .views import views
    from . import events
Miguel Grinberg
  • 65,299
  • 14
  • 133
  • 152