Building a simple WebChat
Tags: webchat, beginners, python, websockets
Published: 2019-09-10 00:22:07

---
title: Building a simple WebChat
published: true
description: Learn how to create a simple webchat with python and flask
tags: webchat, beginners, python, websockets
cover_image: https://thepracticaldev.s3.amazonaws.com/i/34lny5gljmzi2ubsg7g9.jpg
---

Hello there. In this post, you are going to learn how to build a simple webchat using python and web sockets. Since that's the case, you are going to need to have [python](https://www.python.org/) installed on your computer.

This project source code is available on this [github repository](https://github.com/raymag/papo-reto) and you can see it online at [Papo Reto](https://papo-io.herokuapp.com/).

If you don't have the `flask` package, you can install it with the following command: `pip install Flask`. You'll also need `flask_socketio` and `eventlet`, so you can run `pip install flask_socketio` and `pip install eventlet` to install them.

Now that you have everything installed, this is how the files and directories of our project will look like:
```
templates
 - index.html
static
   css
    - style.css
- main.py
```

The core of our project is the `main.py` file, that's what will control our application. It will look like this:

```python
#! -*- enconding: utf-8 -*-
from flask import Flask, render_template
from flask_socketio import SocketIO, emit

app = Flask(__name__, template_folder='templates', static_folder='static', static_url_path='/static/')
socketio = SocketIO(app)

@app.route('/')
def index():
    return render_template('index.html')

@socketio.on('client_message')
def receive_message (client_msg):
    emit('server_message', client_msg, broadcast=True)

if __name__ == '__main__':
    socketio.run(app)
```

The first two lines of code import the packages and modules we are going to use in this project. 

```python
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
```

`Flask` is our web framework, and `flask_socketio` is a package that allows `flask` to use full-duplex low-latency communications protocols like websockets.

Then we instantiate our flask application and define where the templates and static files are going to be. We also instantiate our socketio object and pass the flask application as a argument.

```python
app = Flask(__name__, template_folder='templates', static_folder='static', static_url_path='/static/')
socketio = SocketIO(app)
```

We now define our default route which is going to return the `index.html` page:

```python
@app.route('/')
def index():
    return render_template('index.html')
```

When the user sends a message, it will emit the event client_message to the server, passing the nickname and the message as parameters. The server will then broadcast this message to every connected user:
```python
@socketio.on('client_message')
def receive_message (client_msg):
    emit('server_message', client_msg, broadcast=True)
```
Finally, we'll start to run our application with the following lines:
```python
if __name__ == '__main__':
    socketio.run(app)
```

That's it, our `main.py` file is now complete, and that take us to the `index.html` file. In this case, it will have 3 major parts: the `message-box`, the `input-box` and the `nickname-box`. The contents of this file will be:

```html
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<title>Papo Reto</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js" integrity="sha256-yr4fRk/GU1ehYJPAs8P4JlTgu0Hdsp4ZKrx8bDEDC3I=" crossorigin="anonymous"></script>

</head>
<body>
<script type="text/javascript" charset="utf-8">
const socket = io();
socket.on('server_message', (data) => {
	let e = document.createElement('p');
	let sp = document.createElement('span');
	sp.innerHTML = data.nickname;
	e.appendChild(sp);
	e.innerHTML = e.innerHTML+'>> '+data.message;
	if(document.getElementById('message-box').children.length>20){
		document.getElementById('message-box').removeChild(document.getElementById('message-box').children[0]);
	}
	document.getElementById('message-box').appendChild(e);
	
	document.getElementById('message-box').scroll(0, document.getElementById('message-box').scrollHeight);
});
function htmlEntities(str) {
    return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
function sendMessage(){
	socket.emit('client_message', {'nickname': htmlEntities(document.getElementById('nickname-input').value), 'message':  htmlEntities(document.getElementById('message-input').value)});
	document.getElementById('message-input').value = '';
}
function isPressingEnter(e){
	let k;
	if(window.event){
		k = e.keyCode;
		if(k===13){
			sendMessage();
		}
	}else if(e.which){
		k = e.which;
		if(k===13){
			sendMessage();
		}
	}
}
</script>

<h1 id="title">Papo Reto</h1>
<section id="chat-box">
	<section id="message-box">
	</section>
	
	<section id="input-box">
		<input type="text" autofocus onkeypress="return isPressingEnter(event)" required placeholder="Digite sua mensagem aqui" id="message-input">
		<button type="button" id="send-button" onclick="sendMessage()" >>></button> 
	</section>
</section>

<section id="nickname-box">
	<label id="nickname-label" for="nickname-input">Nickname: </label>
	<input type="text" id="nickname-input" autocomplete="off" value="Guest">
</section>

</body>
</html>
```

We are using the [SocketIO](https://socket.io/) client side api to connect and take care of handling the websockets. So in this case, we are using a CDN to do so:

```html
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js" integrity="sha256-yr4fRk/GU1ehYJPAs8P4JlTgu0Hdsp4ZKrx8bDEDC3I=" crossorigin="anonymous"></script>
```

Now, with javascript, we need to define our socket object, that will connect with the server:
```javascript
const socket = io();
```
Since the websocket is on the same route, we don't need to pass anything as parameters.

When the user clicks on the send button, it will call the `sendMessage` function. But if the user types something on the input with the `input-box` id, it will call the `isPressingEnter` function, which will verify if the user is pressed enter. If so, it will also call `sendMessage()`:

```javascript
function isPressingEnter(e){
	let k;
	if(window.event){
		k = e.keyCode;
		if(k===13){
			sendMessage();
		}
	}else if(e.which){
		k = e.which;
		if(k===13){
			sendMessage();
		}
	}
}
```
 The `sendMessage` function will then emit a `client_message` event to the server, passing the nickname and the message as data:
```javascript
function sendMessage(){
	socket.emit('client_message', {'nickname': htmlEntities(document.getElementById('nickname-input').value), 'message':  htmlEntities(document.getElementById('message-input').value)});
	document.getElementById('message-input').value = '';
}
```
We're also using the `htmlEntities` function to remove the HTML tags from the inputs:
```javascript
function htmlEntities(str) {
    return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
}
```

When the server receives the message, it will broadcast the message to everybody by emiting the `server_message` event. So we need to add this message to the `message-box` when the `server_message` event is received:
```javascript
socket.on('server_message', (data) => {
	let e = document.createElement('p');
	let sp = document.createElement('span');
	sp.innerHTML = data.nickname;
	e.appendChild(sp);
	e.innerHTML = e.innerHTML+'>> '+data.message;
	if(document.getElementById('message-box').children.length>20){
		document.getElementById('message-box').removeChild(document.getElementById('message-box').children[0]);
	}
	document.getElementById('message-box').appendChild(e);
	
	document.getElementById('message-box').scroll(0, document.getElementById('message-box').scrollHeight);
});
```

If you now, run the server with `python main.py`, you'll see our webchat is already working.
![WebChat running without CSS](https://thepracticaldev.s3.amazonaws.com/i/w9n2c7gqygq036knv6w2.jpg)

But since it's looking terrible, we'll add some css to it. Flask templates uses `jinja` as its template engine, so it's a little bit different to add our css file path to the `index` page. We can do so adding the following lines of code inside the `head` tag:
```html
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='./css/stylxe.css') }}">
```

You can do as you please, but in this case, that's the contents of our CSS file:
```css
*{
	margin: 0;
	padding: 0;
}
body{
	background: #202020;
	color: #eee;
	font-family: 'monospace';
	line-height: 1.4em; 
	display: flex;
	flex-direction: column;
	justify-content: space-around;
	align-items: center;
	padding: 15px;
}
#title{
	font-size: calc(20px + 2vw + 1vh);
	padding: 40px;
}
#chat-box{
	border: 2px solid #ccc;
	border-radius: 10px;
	box-shadow: 3px 4px 4px #111;
}
@media only screen and (max-width: 600px){
	#chat-box{
		width: 95vw;
	}
}
@media only screen and (min-width: 600px){
	#chat-box{
		width: 80vw;
	}
}
#message-box{
	width: 100%;
	height: 60vh;
	background: #444;
	overflow-y: scroll;
	padding: 5px;
	font-size: calc(10px + 1vh + 2vw);
}
#message-box p{
	width: 100%;
	line-height: 1.5em;
	overflow-wrap: break-word;
	padding: 5px 2px;
	margin: 2px 0;
	background: #4f4f4f;
}
#message-box span{
	color: yellow;
	font-weight: 900;
}

#input-box{
	width: 100%;
	height: 100%;
	display: grid;
	grid-template-columns: 90% 10%;
}
#message-input{
	height: 90%;
	font-size: calc(10px + 1vh + 2vw);
	border-radius: 0 0 0 6px;
}
#send-button{
	background: purple;
	color: #f0f0a0;
	height: 100%;
	font-size: calc(10px + 1vh);
	font-weight: 900;
}
#nickname-box{
	padding: 15px;
}
#nickname-input {
	border-radius: 5px;
	background: purple;
	color: white;
	padding: 2px;
	font-size: calc(10px + 1vh + 1vw);
}
#nickname-label{
	font-size: calc(10px + 1vh + 1vw);

}
```

And now, that's the result:
![Running WebChat](https://thepracticaldev.s3.amazonaws.com/i/34lny5gljmzi2ubsg7g9.jpg)

Thank you for your attention! That's one of the ways we can build a really simple webchat using python. Also, it's pretty easy to do so, but if you guys want, I can also show how to deploy it to heroku. So you can let it run online.