Chat App Using Flask, SocketIO and Google Colab¶
In this short tutorial, you'll learn how to create a simple but elegant chat app and host it on Google Cloud (for free) under 5 minutes .
Here's how the App would look like:
On Mobile:
On Browser:
What you'll build¶
- A fully working real-time multi user chat app.
- Mobile and web friendly Progressive Web App (PWA).
- Public URL to share with family/friends.
- Hosted on Google Cloud for free.
What you'll learn¶
- Dveloping a useful Flask web app.
- Using WebSocket with Flask-SocketIO for bi-directional real-time communication.
- How to create a public URL with ngrok to share your web app.
- How to run a web server on Colab.
What you'll need¶
- Gmail account to access Google Colaboratory for free.
- A browser such as Chrome.
- The sample notebook. Click on the Open in Colab button below to get started.
%%writefile requirements.txt
Flask==0.12.2
flask-socketio
eventlet==0.17.4
gunicorn==18.0.0
Flask App¶
Here you are defining event handlers to receive WebSocket (ws
) messages using the socketio.on
decorator and it sends reply messages to the connected client using the send()
and emit()
functions.
Also you are defining a simple http
endpoint /hello
for testing purpose.
%%writefile main.py
import os
import logging
from flask import Flask, render_template
from flask_socketio import SocketIO
secret = os.urandom(24).hex()
app = Flask(__name__)
app.logger.info("Starting...")
app.config['SECRET_KEY'] = secret
app.logger.critical("secret: %s" % secret)
socketio = SocketIO(app)
# render home page
@app.route('/')
def index():
return render_template('index.html')
# Simple http endpoint
@app.route('/hello')
def hello():
return "Hello World!"
# ws callback
def message_received(methods=['GET', 'POST']):
app.logger.info('message was received!')
# ws event handler
@socketio.on('flask-chat-event')
def handle_flask_chat_event(json, methods=['GET', 'POST']):
app.logger.info('received flask-chat-event: ' + str(json))
socketio.emit('flask-chat-response', json, callback=message_received)
if __name__ == '__main__':
socketio.run(app, debug=True)
HTML Template¶
Here you are defineing:
- Web UI for the app.
- JavaScript functions to establish
ws
connection, send and receive messages withsocket.io
.
%mkdir templates -p
%%writefile templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta content="IE=edge" http-equiv="X-UA-Compatible"/>
<meta content="Learn how to create a chat app using Flask" name="description"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>Flask Chat</title>
<!-- Disable tap highlight on IE -->
<meta content="no" name="msapplication-tap-highlight"/>
<!-- Web Application Manifest -->
<link href='data:application/manifest+json,{ "name": "Flask Chat", "short_name": "Flask Chat", "display": "standalone" }' rel="manifest"/>
<!-- Add to homescreen for Chrome on Android -->
<meta content="yes" name="mobile-web-app-capable"/>
<meta content="Flask Chat" name="application-name"/>
<meta content="#303F9F" name="theme-color"/>
<!-- Add to homescreen for Safari on iOS -->
<meta content="yes" name="apple-mobile-web-app-capable"/>
<meta content="black-translucent" name="apple-mobile-web-app-status-bar-style"/>
<meta content="Flask Chat" name="apple-mobile-web-app-title"/>
<meta content="#303F9F" name="apple-mobile-web-app-status-bar-style"/>
<!-- Tile icon for Win8 -->
<meta content="#3372DF" name="msapplication-TileColor"/>
<meta content="#303F9F" name="msapplication-navbutton-color"/>
<script async="" src="https://www.googletagmanager.com/gtag/js?id=UA-135532366-1"></script>
<script>function gtag(){dataLayer.push(arguments)}window.dataLayer=window.dataLayer||[],gtag("js",new Date),gtag("config","UA-135532366-1");</script>
<!-- Material Design Lite -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/>
<link href="https://code.getmdl.io/1.1.3/material.orange-indigo.min.css" rel="stylesheet"/>
<script defer="" src="https://code.getmdl.io/1.1.3/material.min.js"></script>
<!-- App Styling -->
<link href="https://fonts.googleapis.com/css?family=Roboto:regular,bold,italic,thin,light,bolditalic,black,medium&lang=en" rel="stylesheet"/>
<style>
.message-container .spacing {
display: table-cell;
vertical-align: top;
}
.message-container .message {
display: table-cell;
width: calc(100% - 40px);
padding: 5px 0 5px 10px;
}
.message-container .name {
display: inline-block;
width: 100%;
padding-left: 40px;
color: #bbb;
font-style: italic;
font-size: 12px;
box-sizing: border-box;
}
</style>
</head>
<body>
<div class="demo-layout mdl-layout mdl-js-layout mdl-layout--fixed-header">
<!-- Header section containing logo -->
<header class="mdl-layout__header mdl-color-text--white mdl-color--light-blue-700">
<div class="mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-grid">
<div class="mdl-layout__header-row mdl-cell mdl-cell--12-col mdl-cell--12-col-tablet mdl-cell--12-col-desktop">
<h3><i class="material-icons">chat_bubble_outline</i> Flask Chat</h3>
</div>
</div>
</header>
<main class="mdl-layout__content mdl-color--grey-100">
<div class="mdl-cell mdl-cell--12-col mdl-grid" id="messages-card-container">
<!-- Messages container -->
<div class="mdl-card mdl-shadow--2dp mdl-cell mdl-cell--12-col mdl-cell--6-col-tablet mdl-cell--6-col-desktop" id="messages-card">
<div class="mdl-card__supporting-text mdl-color-text--grey-600">
<div id="messages">
<span id="message-filler"></span>
<div class="message-container visible">
<div class="spacing">
</div>
<div class="message">Welcome!</div>
<div class="name">Shanaka DeSoysa</div>
</div>
</div>
<form action="POST" id="message-form">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" id="username" type="text"/>
<label class="mdl-textfield__label" for="username">User...</label>
</div>
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input class="mdl-textfield__input" id="message" type="text"/>
<label class="mdl-textfield__label" for="message">Message...</label>
</div>
<button class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect" id="submit" type="submit">
Send
</button>
</form>
</div>
</div>
</div>
</main>
</div>
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.7.3/socket.io.min.js"></script>
<script type="text/javascript">
var socket = io.connect('https://' + document.domain + ':' + location.port);
socket.on('connect', function () {
socket.emit('flask-chat-event', {
data: 'User Connected'
})
var form = $('form').on('submit', function (e) {
e.preventDefault()
let user_name = $('#username').val()
let user_input = $('#message').val()
socket.emit('flask-chat-event', {
user_name: user_name,
message: user_input
})
$('#message').val('').focus()
})
})
socket.on('flask-chat-response', function (msg) {
console.log(msg)
if (typeof msg.user_name !== 'undefined') {
$('#messages').append('<div class="message-container visible"><div class="spacing"></div><div class="message">' + msg.message + '</div><div class="name">' + msg.user_name + '</div></div>')
}
})
</script>
</body>
</html>
Installing Packages and Running Web Server¶
get_ipython().system_raw(
'pip3 install -r requirements.txt && python3 main.py > logs.txt 2>&1 &'
)
Checking the Log File¶
You can check the log file with this command.
!tail logs.txt
Verifying the Web Server is Running¶
You can do a quick check if the server is up and running with curl
.
# Make sure it's running on local port
PORT = 5000
!curl http://localhost:{PORT}/hello
Getting a Shareable Public URL from ngrok¶
Here you are installing ngrok
and obtaining a shareable URL.
!wget --quiet https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip -O ngrok-stable-linux-amd64.zip
!unzip -q -f ngrok-stable-linux-amd64.zip
Running ngrok
.
get_ipython().system_raw(
'./ngrok http {} &'
.format(PORT)
)
Public URL¶
public_url = !curl -s http://localhost:4040/api/tunnels | python3 -c \
"import sys, json; print(json.load(sys.stdin)['tunnels'][0]['public_url'])"
print(public_url[0])
Verifying the Public URL is Accessible¶
!curl {public_url[0]}/hello
Congratulations! You have successfully built a chat application using Flask and WebSockets. You can share this URL with your friends and chat. You could also open multiple tabs in the browser to test it out. Don't forget to check it out on your mobile.
The source-code for the article can be found here.