Chatzilla



This is a step by step tutorial on building a basic real time chat application using Flask, socket.io and gevent-socketio. All the code is available here. You can find live version of the app here.

As of Oct 8 2013, Heroku rolled out Public Beta of Websockets. The official announcement can be found here.

ChatZilla is a 'clean, easy to use and highly extensible IRC client, built on the Mozilla platform'. It is written entirely in JavaScript, supported by XUL, CSS and HTML. It is designed to run on any platform on which Mozilla runs, such as Mac OS, Linux, BSD, Microsoft Windows, Solaris, IRIX, BeOS, AIX, HP-UX, and OS/2. It currently supports most standard features of IRC clients, including. Chatzilla is an excellent extension for this browser that adds some interesting functionalities like the option of chatting through IRC networks. Build a simple IRC client that lets you chat on any network. By default it has ten chat networks, but you can connect to whichever you prefer, as long as you know the URL where its server is hosted.

If you don't enable Websocket functionality on Heroku, Socket.io will fallback to XHR polling.

To enable websockets on your Heroku app, use the following command

Chapter 1: Getting started

I assume you know what Heroku is and you've seen Python before. Prior knowledge of Flask is a plus but given how minimalistic it is, you can just tag along.

Heroku up

First things first, let's create a shiny new appication on Heroku

I called my app 'chatzilla' and got the following output

The git part is important, let's put it to good use. Let's setup a git repository and point it towards heroku

We now have an empty git repository with a remote aliased 'heroku' pointing to the Heroku git for the project. The game is afoot.

Setup basic Flask app

Virtual environment

As every responsible Python developer, you use some kind of virtual environment manager (I personally use virtual env wrapper). Create a virtual environment named 'chatzilla' (or something else) and automatically activate it:

Virtual envs save your mental health and remaining hair, so use them.

Get all the goods using Pip

Pip is great for getting all your jazz organized. We'll be using it to install all the modules we need to make Chatzilla happen.

Adobe acrobat pro xi for mac. Please note we are using a particular version of the gunicor since later versions seem to have certain issues.

Freeze

Once Pip has installed all the modules to our previously pristine virtual environment, it's time to get a snapshot of what's installed so that when you move your app elsewhere, you can easily restore the environment

Your requirements.txt should look something like this

chatzilla.py

Let's create an entry point for the application now

Let's fill it with some Python

In it's current state, our app has none of the promised realtime awesomeness but we'll get there - one step at a time.

run_server.py

In order to run chatzilla, we'll add another python module

It looks like this:

Procfile : Flask meets Heroku

Before a much deserved refreshing beverage, let's tell Heroku how to run our application using Procfile

Inside:

This is basically us saying: could we have a web instance running gunicorn with a worker that can speak socket.io and yeah, check run_server.py for more instructions, por favor.

But will it blend?

Let's see if this works

This should launch a local dev server on your machine. If you head to http://localhost:5000/, you should be able to witness Chatzilla in all its glory.

Let's go global now

This should keep Heroku busy for a few moments. Once it's done processing the push, we can take a look at Chatzilla in the wild

Before you panic

Just in case you are stuck/confused/lazy, you can grab all the code we've produced so far here and keep following along.

Chapter 2: Sockets, por favor

Wire a namespace

Adobe go reader for mac. We currently have a socket.io endpoint exposed to the world and we should start handling various events that will get triggered once people start using it. Head to chatzilla.py and add the following:

Also update import statements on the top of the module:

socketio function maps to the /socket.io/someotherstuff url and this is how the client side of the Chatzilla will try to reach out to the server with some urgent real time goodness. We obviously need to handle that, and we are doing it by proxying the request to ChatNamespace (which we'll create in a second).

Namespaces give us a way to manage various sub categories within a socket.io endpoint. For Chatzilla we will only be using a single such category called 'chat'. Let's look what a namespace definition might look like in our case:

ChatNamespace does not do a lot at this point - it logs connects and disconnects and that's it. Make sure you add the missing import to the chatzilla.py

Client is always right

Let's try to figure out if we can actually connect to the chat server from the client. One step at a time.

Massive redesign

Let's fix our landing page a bit. Start by bringing in templates The best mail program for mac.

Populate landing.html with the following html

Now let's render the template using Flask:

Don't forget imports

Once you run foreman start you should see a shiny new Chatzilla interface. Epic.

Wire the socket

Let's bring socket.io javascript goodness in the mix.

Let's update templates/landing.html to include socket.io

Connect to the server

With socket.io javascript in place, we can now connect to our chat instance. Update landing.html template with the following code

Refresh the page to see the popup. Hooray!

Join the chat

Once connected to the chat server, we should be able to join chat. Let's implement that. Jump back to chatzilla.py and add the following block to the ChatNamespace:

Back to the client, let's update the 'connect' event handler

Notice how return values from the on_join in the ChatNamespace are automatically propagate to the client and we can recover them in the event handler for the emit method.

Send a message

Let's add message sending functionality to the chat service. Start with the backend again

And now the client, let's automatically send a message once someone joins the chat

Broadcast

When a new message arrives to the server, we will want to send it to other people in the system. Let's get back to the ChatNamespace in chatzilla.py and add a BroadcastMixin which will help us do just that

Keeping imports happy

With BroadcastMixin attached to the ChatNamespace, we can update the on_message handler to look like this

We can now update the client to look something like this

If you now have a couple of tabs open in your browser you'll see an alert box with a greeting from Bob.

Let's deploy

Tons of users out there are looking forward to experiencing the new version of Chatzilla. Commit your changes, and push.

Before you panic

As before, you can grab the code for this chapter here

Chapter 3 : UI

Now that we have a functioning client-server communication model, let's add some UI on top of that so that people can control what data gets sent to the server + get rid of alert popups and display data more gracefully.

Move some stuff around

Let's isolate client side code in a separate module so we can stop poluting the landing page template

Let's move all the inline js from the landing template to chatzilla.js so it looks like this

The landing template should now look something like this

Add join chat UI

Let's add a little join chat form in a section between the header and the footer of the landing page

Before we wire the handlers for the form, let's grab [jquery validation plugin])(http://jqueryvalidation.org/) we can put ot good use here. Place this right after the jquery script tag.

Let's update chatzilla.js to handle form submission + validation

Let's also start organizing our socket interactions so we can easily call our chat server from various UI event handlers. Here's how this little submodule could look like (with 2 methods for now):

Chatzilla

And as we update the rest of chatzilla.js:

Commit what you have and let's move on.

Add Send Message UI

Once the connection is established, let's hide the join form and show some kind of message composer:

Our chatAPI module will need a new method (sendMessage):

And here's what chatzilla.js looks like now

Notice, how we are handling the message form in a similar way + we toggle visibility on both forms after joining chat.

Commit what you have and let's move on.

Add Message List UI

As any respectable Chat application out there Chatzilla needs a list of messages that people are exchanging. Let's build it.

First, let's figure out how this impacts our chatApi module. We'll have chatAPI trigger a handler once a new message is received:

And then in the bindUI, we can do the following:

chatzilla.js now looks like this

Let's get rid of the alerts by creating a container for the messages in the landing.html. After we update the section of the landing.html it looks like this

Notice that the message list is initially invisible. We'll show it once a person joins the chat (just like with the message form)

Let's update the onMessage handler so that it displays the message within the list:

Checkout the result (2+ tabs). Commit, grab a drink.

More context

Current version of Chatzilla has a number of issues. The most noticeable one is that you can't really tell who the messages you receive come from. Let's fix that.

Session

We can take advantage of session data to keep track of the message sender. Let's modify on_join handler for the ChatNamespace in chatzilla.py

This will allow to keep track of who's sending a message and we can use this data when we broadcast messages to others in the chat

Tell the client

We can now update chatzilla.js to take advantage of this additional information

Let's also make sure your own message are added to the list once sent:

Commit what you have. And push what you have to heroku.

Chatzilla Firefox 2.0

Before you panic

All the code for chapter 3 is here if you need it.

Chapter 4: More UI

Chatzilla.com

The final version of the app is available here. You'll notice it's quite different from where we left in Chapter 3. All the code is available here.