How to write your own chat dizmo with socket.io

This post is a response to the following question that we have being asked in one form or another:

How can a dizmo reach out to connect with 3rd party systems? Or, is there a way that a dizmo can communicate with a server?

The short answer is: Dizmo uses Webkit as its basis to render dizmos. This means it is possible to use Ajax to connect to other systems like you would do in any browser. Webkit is also able to setup Websocket based calls if a more elaborate two way communication is needed to access a service.

For those with a hands on approach or who want to go in depth, we have prepared a tutorial on how to write a chat dizmo with socket.io.

Our chat dizmo tutorial is based on the one provided by socket.io.

Server (node.js)

We assume that you have node.js and npm already installed!

First create a package.json manifest file that describes our the project. We recommend that you place it in an empty directory (we will call ours chat-io).

{
    "version": "0.0.1",
    "description": "my first socket.io app",
    "dependencies": {}
}

In order to easily populate the dependencies with the required objects, we’ll use npm install --save:

$ npm install --save express@4.10.2

Now that express is installed we can create an ‘index.js’ file that will setup our application.

var app = require('express')();
var http = require('http').Server(app);
http.listen(3000, function(){
    console.log('listening on *:3000');
});

This translates into the following: Express initializes app to be a function handler that you can supply to an HTTP server (as seen in line 2). We make the http server listen on port 3000. Open a new Terminal window and run node index.js. This starts up the node server. You should see the following:

Integrating Socket.IO

$ npm install --save socket.io

The command above installs the module and adds the dependency to package.json. This installation process might take a while.

Let’s continue by changing index.js to the following code:

var app = require('express')();
var http = require('http').Server(app);

var io = require('socket.io')(http);
io.on('connection', function(socket){
    console.log('a user connected');
    socket.on('disconnect', function(){
        console.log('user disconnected');
    });
});

http.listen(3000, function(){
    console.log('listening on *:3000');
})

Notice that we initialize a new instance of socket.io by passing the http (the HTTP server) object. Then we listen on the connection event for incoming sockets, and log it to the console. Each socket also fires a special disconnect event.

Dizmo

We create our index.html file and add the socket.io and jquery libraries as well as the styles to the head section.

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8">
            <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.js"></script>
            <script src="https://cdn.socket.io/socket.io-1.2.1.js"></script>
            <script src="application.js"></script>
            <style>
                * { margin: 0; padding: 0; box-sizing: border-box; }
                body { font: 13px Helvetica, Arial; }
                form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
                form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
                form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
                #messages { list-style-type: none; margin: 0; padding: 0; }
                #messages li { padding: 5px 10px; }
                #messages li:nth-child(odd) { background: #eee; }
                </style>
            </head>
    <body>
        <div id="front">
            <ul id="messages"></ul>
            <form action="">
                <input id="m" autocomplete="off" /><button>Send</button>
            </form>
        </div>

        <div id="back">
        </div>
    </body>
</html>

Our application.js will initialize socket.io.

function showFront() {
    dizmo.showFront();
}

function showBack() {
    dizmo.showBack();
}

dizmo.onShowBack(function() {
    // You can add your own code here that will be executed when the back side is shown.
});

dizmo.onShowFront(function() {
    // You can add your own code here that will be executed when the front side is shown.
});

document.addEventListener('dizmoready', function() {
    console.log("ready");
    var socket = io('http://localhost:3000');
});

As our server is running on localhost on port 3000, we need to specify that here.

Lastly, create Info.plist and paste the following content into it:

<?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>ApiVersion</key>
        <string>1.2</string>
        <key>BundleDisplayName</key>
        <string>chat-io</string>
        <key>BundleIdentifier</key>
        <string>com.dizmo.chat-io</string>
        <key>BundleName</key>
        <string>chat-io</string>
        <key>BundleShortVersionString</key>
        <string>0.1</string>
        <key>BundleVersion</key>
        <string>0.1</string>
        <key>ElementsVersion</key>
        <string>1.0</string>
        <key>Height</key>
        <integer>300</integer>
        <key>MainHTML</key>
        <string>index.html</string>
        <key>MinSpaceVersion</key>
        <string>0.0.0</string>
        <key>Width</key>
        <integer>250</integer>
        <key>HiddenDizmo</key>
        <false/>
    </dict>
</plist>

Now, open the dizmo and you’ll see in the server console that a user has connected.

When you reload the dizmo from the dizmo menu (make sure development mode is enabled) you’ll see the user disconnecting and reconnecting. Also try to open an additional instance of the chat dizmo, and check in the server console that two users are connecting!

Emitting events

The main idea behind Socket.IO is that you can send and receive any events you want, with any data you want. Any javascript objects that can be encoded as JSON will be, and binary data is supported too.

Let’s make it so that when the user types in a message, the server gets it as a chat message event. Add the following code to the function document.addEventListener application.js:

$('form').submit(function(){
    socket.emit('chat message', $('#m').val());
    $('#m').val('');
    return false;
});

Broadcasting

The next goal is for us to emit the event from the server to the rest of the users.

In order to send an event to everyone, Socket.IO gives us the io.emit:

io.emit('some event', { for: 'everyone' });

If you want to send a message to everyone except for a certain socket, we have the broadcast flag:

socket.broadcast.emit('hi');

In this case, for the sake of simplicity we’ll send the message to everyone, including the sender. Add the following code to index.js:

socket.on('chat message', function(msg){
    io.emit('chat message', msg);
});

Every time we write a text and press the send button, a message is sent to the server, which in turns sends it back to all the connected clients, which will display it using the following code in application.js:

socket.on('chat message', function(msg){
    $('#messages').append($('&lt;li&gt;').text(msg));
})

Now, you can chat between 2 (or more) dizmos! This completes our chat server and dizmo, in about 50 lines of code!

Homework

Here are some ideas to improve the application:

  • Broadcast a message to connected users when someone connects or disconnects
  • Add support for nicknames
  • Don’t send the same message to the user that sent it himself. Instead, append the message directly as soon as he presses enter
  • Add “{user} is typing” functionality
  • Show who’s online
  • Add private messaging
  • Share your improvements!
  • Tell us, what tutorial you want to read

Get the source code

You can find the source code on Github

$ git clone https://github.com/dizmo/chat-io.git

Leave a Reply

Your email address will not be published. Required fields are marked *