The node rewrite of
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


CompassionPit is a chat application that pairs two people anonymously. There is always two people to a conversation, each with distinct roles. One is a "venter", this person is the one who is seeking help/compassion/someone to talk to. The other is a "listener", whose role is to listen, provide support, and just be someone to be talked to.

From a technical standpoint, the application works as such:

A person goes to the site, and they click on a link to either become a venter or a listener. For this example, let's assume that the person decided to be a venter.

They visit the "/vent" page, which will then form a connection with Socket.IO to the server, sending preliminary information in its first message, of type "register". The server will then assign the client a private and public user IDs.

Since internet connections can be shoddy, we want to support arbitrary disconnect/reconnect support with the server, as long as the client isn't disconnected for more than 10 seconds at a time, then they should be able to hold their user account. Assuming the client disconnects, if they reconnect, the server can't know that they are the same user as before, but they can send their "register" message and provide their private and public user IDs to the server to retain their user. As long as they do this within the disconnect leeway period (10 seconds), it'll act to the system and the client as if they had never disconnected. When reconnecting, the "register" message sent will include the index of the last message received, and the message sent back will include the server's last received message index. The client will then send any backlogged messages, and the server will do the same, so each can catch up to the exact point and no data is lost.

Once they are registered with the server, they then request to "join" a room, providing the information about what type of user they are, either "venter" or "listener". The server then receives this message and places them in the appropriate queue. If there is a free venter and listener available at this point, a room will be created for them and they can start chatting. While the user waits for a room, they can request what queue position they are in, which will calculate where they are in the queue and then show this information to the user. This updates every few seconds.

Once the "join" message is relayed to the user, this means that they have officially joined a room and are able to chat with their partner. The partner's public user ID is provided with this message and, if possible, the geo-location info will appear, providing each partner with information about where the other is so as to prevent any awkward tension or provide context.

When in a room, an input box will appear for each of them which they can type into. Once they hit enter, a message is sent to the server which is then relayed to their partner. Their message will immediately show in their log, without the server sending a confirm message back.

When a user receives a "msg" event, this means their partner has sent them a message, which will be displayed in their log.

When a user receives a "sysmsg" event, this means a system administrator has sent out a message, which will be shown in their log with the other messages.

A user can request a new chat partner at any point in time after 15 seconds of conversation with their partner (to prevent repeatedly requesting new partners). Once this occurs, the server will mark that the two users have had an interaction and will prevent them from interacting in the future. Each will re-enter their respective queues and the queue processor will try to match them up with someone else. Since they have had an interaction, the queue processor will explicitly prevent pairing those two up again.

If a user disconnects, either by having a shoddy internet connection, or closing their browser, after the 10-second disconnect leeway, it will inform the remaining user that their partner disconnected and re-queues them into the system, allowing the process to repeat.

If the server application were to be restarted, thus losing all its in-memory data, it appears to the users as though they disconnect, and try to re-register under their old user IDs. The server then accepts this data and creates this "new" user profile. They also provide the public user ID of their partner and are given 15 seconds to reconnect with their partner. Assuming both users provide this data, they will be automatically re-paired with each other, allowing them to continue their conversations.

CompassionPit--Node- notes

  • forked the project in github and cloned it to have a local version to work on.

    git clone<username>/CompassionPit--Node-.git

To get project to run:

  • install XCode4 - this will give you the Carbon and gcc/g++ tools needed to compile node.js later

  • install MongoDB:

  • install node.js ( Please use with version 0.60 or greater..

  • install npm (node package manager). one line install:

    curl | sh

  • copy "config-sample.js" to "config.js". config.js is in the .gitignore file, which git tracks. "static/style.min.css" and "static/script.min.js" are also included in ".gitignore"; those are automatically generated and minified when the application is run.

  • fill out config.js according to your server

  • install GeoIP lib:

    wget tar -xvzf GeoIP-1.4.7.tar.gz cd GeoIP-1.4.7 ./configure make sudo make install

  • npm install geoip

  • NODE_ENV=development node app.js

  • you also need to run the leaderboard calculator, which you should run as a separate process: NODE_ENV=development node leaderboard.js

  • N.B. if after running "node app.js", the application complains about you missing any dependencies (e.g. "error, couldn't find module 'mongoose'"), you should be able to install them by simply doing "npm install mongoose"

  • i like to keep the app running with forever start app.js and forever start leaderboard.js. you may need to install the npm "gem" forever, first: npm install forever -g.


  • url of 'venter' or 'listener' renders views/chat.jade

  • i didn't know what 'use strict' means or does, so i looked it up: It catches some common coding bloopers, throwing exceptions. It prevents, or throws errors, when relatively "unsafe" actions are taken (such as gaining access to the global object). It disables features that are confusing or poorly thought out. "not in Firefox 3.6, Safari 5, Chrome 7 and Opera 10.6 (all Mac). Didn't test in IE9 though ;) – Nov 10 '10
    Firefox 4 has complete support for strict mode, and as far as I can tell, no other browser does. Safari and Chrome have "partial" support, but I don't really know what that means."

  • took a few minutes to figure out that socketHanders class is not defined somewhere; it is created on the fly in mergeServers() in app.js.

  • not seeing how button was implemented in chat.jade until i realized it is in static/chat.js.

  • finally dawned that 'type' in chat.jade line 'window.CLIENT_TYPE = !{JSON.stringify(type)};' is passed in with the render() call.

  • Rooms class defined in rooms/models.js. The queues for venters/listeners are there.

  • When a user is added to room, 'Room.addUser()' is called; which sends both users a 'join' message with geoInfo; which pushes message to queue and flushes queue; which sends message to client using id from app.socket.clients[].

  • .lookupGeoIP(callback) is called to get geo info.

  • .send(message) is called to have message displayed in users chat window.

  • added an id to the status text input and focus on it when window is activated. i'm not sure if that fixes the problem of having the field be in focus after clicking the 'find new partner' button.

Not Understood:

  • what is the config item 'serveMerged' mean? it is set 'true' in config.

    If serveMerged is true, it will merge and minify the JS and CSS files and serve the merged version rather than a series of individual ones. In production, it makes sense to merge them, in development, it'd be an undue burden.

  • whole calls 'mergeStatic(function())'.

    This is asynchronous javascript in action, the callback specified will be called after the merging and minification of the JS and CSS.

  • so chat.jade is source for html, but no header tag or linking of js files or css file? how does jade know to use static/chat.js and static/css/style.css? is that a feature of jade?

    layout.jade handles this.

  • where are calls to socketHandlers.register()?

    when the client sends a message with the "register" type, it calls socketHandlerstype


There currently is no UI for reading or manipulating the logs, but it's relatively easy to do so in mongo.

SSH into the server, then launch mongo.

Then type in use log-p, since we want to switch to the "log-p" database.

From this point on, you can manipulate the db.conversations collection.

Fetch all conversations


Fetch active conversations

db.conversations.find({ status: "active" });

Fetch all conversations, order by start time

db.conversations.find({ $query: {}, $orderby: ["startTime"] });

Fetch conversations within a time window

db.conversations.find({ startTime: { $gt: new Date(12345678), $lt: new Date(23456789) } });

Fetch status and messages of conversations

    .map(function (c) {
        return {
            status: c.status,
            messages: (m) {
                return m.partner + ": " + m.text;