Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
tegioz committed Sep 29, 2012
0 parents commit 1e15a0e
Show file tree
Hide file tree
Showing 24 changed files with 11,706 additions and 0 deletions.
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# SimpleChat

Built with:

- <strong>Server side:</strong> Node.js, Socket.io, Express, Redis
- <strong>Client side:</strong> HTML5 Boilerplate, Bootstrap, Handlebars and jQuery

If you just want to see it running, visit: http://www.tegioz.com:8888

### Requires

- Node.js
- NPM (Node Package Manager)
- Redis

### Get the code

git clone [email protected]:tegioz/chat.git

### Run

Fetch dependencies:

npm install

Launch Redis:

redis-server

Launch chat server:

(don't forget to launch Redis before!)

node chatServer.js

Now open this URL in your browser:

http://localhost:8888/

and you're done ;)
264 changes: 264 additions & 0 deletions chatServer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@

// Author: Sergio Castaño Arteaga
// Email: sergio.castano.arteaga@gmail.com

// ***************************************************************************
// General
// ***************************************************************************

var conf = {
port: 8888,
debug: false,
dbPort: 6379,
dbHost: '127.0.0.1',
dbOptions: {},
mainroom: 'MainRoom'
};

// External dependencies
var express = require('express'),
http = require('http'),
socketio = require('socket.io'),
events = require('events'),
_ = require('underscore'),
redis = require('redis'),
sanitize = require('validator').sanitize;

// HTTP Server configuration & launch
var app = express(),
server = http.createServer(app),
io = socketio.listen(server);
server.listen(conf.port);

// Express app configuration
app.configure(function() {
app.use(express.bodyParser());
app.use(express.static(__dirname + '/static'));
});

// Socket.io store configuration
var RedisStore = require('socket.io/lib/stores/redis'),
pub = redis.createClient(conf.dbPort, conf.dbHost, conf.dbOptions),
sub = redis.createClient(conf.dbPort, conf.dbHost, conf.dbOptions),
db = redis.createClient(conf.dbPort, conf.dbHost, conf.dbOptions);
io.set('store', new RedisStore({
redisPub: pub,
redisSub: sub,
redisClient: db
}));
io.set('log level', 1);

// Logger configuration
var logger = new events.EventEmitter();
logger.on('newEvent', function(event, data) {
// Console log
console.log('%s: %s', event, JSON.stringify(data));
// Persistent log storage too?
// TODO
});

// ***************************************************************************
// Express routes helpers
// ***************************************************************************

// Only authenticated users should be able to use protected methods
var requireAuthentication = function(req, res, next) {
// TODO
next();
};

// Sanitize message to avoid security problems
var sanitizeMessage = function(req, res, next) {
if (req.body.msg) {
req.sanitizedMessage = sanitize(req.body.msg).xss();
next();
} else {
res.send(400, "No message provided");
}
};

// Send a message to all active rooms
var sendBroadcast = function(text) {
_.each(_.keys(io.sockets.manager.rooms), function(room) {
room = room.substr(1); // Forward slash before room name (socket.io)
// Don't send messages to default "" room
if (room) {
var message = {'room':room, 'username':'ServerBot', 'msg':text, 'date':new Date()};
io.sockets.in(room).emit('newMessage', message);
}
});
logger.emit('newEvent', 'newBroadcastMessage', {'msg':text});
};

// ***************************************************************************
// Express routes
// ***************************************************************************

// Welcome message
app.get('/', function(req, res) {
res.send(200, "Welcome to chat server");
});

// Broadcast message to all connected users
app.post('/api/broadcast/', requireAuthentication, sanitizeMessage, function(req, res) {
sendBroadcast(req.sanitizedMessage);
res.send(201, "Message sent to all rooms");
});

// ***************************************************************************
// Socket.io events
// ***************************************************************************

io.sockets.on('connection', function(socket) {

// Welcome message on connection
socket.emit('connected', 'Welcome to the chat server');
logger.emit('newEvent', 'userConnected', {'socket':socket.id});

// Store user data in db
db.hset([socket.id, 'connectionDate', new Date()], redis.print);
db.hset([socket.id, 'socketID', socket.id], redis.print);
db.hset([socket.id, 'username', 'anonymous'], redis.print);

// Join user to 'MainRoom'
socket.join(conf.mainroom);
logger.emit('newEvent', 'userJoinsRoom', {'socket':socket.id, 'room':conf.mainroom});
// Confirm subscription to user
socket.emit('subscriptionConfirmed', {'room':conf.mainroom});
// Notify subscription to all users in room
var data = {'room':conf.mainroom, 'username':'anonymous', 'msg':'----- Joined the room -----', 'id':socket.id};
io.sockets.in(conf.mainroom).emit('userJoinsRoom', data);

// User wants to subscribe to [data.rooms]
socket.on('subscribe', function(data) {
// Get user info from db
db.hget([socket.id, 'username'], function(err, username) {

// Subscribe user to chosen rooms
_.each(data.rooms, function(room) {
room = room.replace(" ","");
socket.join(room);
logger.emit('newEvent', 'userJoinsRoom', {'socket':socket.id, 'username':username, 'room':room});

// Confirm subscription to user
socket.emit('subscriptionConfirmed', {'room': room});

// Notify subscription to all users in room
var message = {'room':room, 'username':username, 'msg':'----- Joined the room -----', 'id':socket.id};
io.sockets.in(room).emit('userJoinsRoom', message);
});
});
});

// User wants to unsubscribe from [data.rooms]
socket.on('unsubscribe', function(data) {
// Get user info from db
db.hget([socket.id, 'username'], function(err, username) {

// Unsubscribe user from chosen rooms
_.each(data.rooms, function(room) {
if (room != conf.mainroom) {
socket.leave(room);
logger.emit('newEvent', 'userLeavesRoom', {'socket':socket.id, 'username':username, 'room':room});

// Confirm unsubscription to user
socket.emit('unsubscriptionConfirmed', {'room': room});

// Notify unsubscription to all users in room
var message = {'room':room, 'username':username, 'msg':'----- Left the room -----', 'id': socket.id};
io.sockets.in(room).emit('userLeavesRoom', message);
}
});
});
});

// User wants to know what rooms he has joined
socket.on('getRooms', function(data) {
socket.emit('roomsReceived', io.sockets.manager.roomClients[socket.id]);
logger.emit('newEvent', 'userGetsRooms', {'socket':socket.id});
});

// Get users in given room
socket.on('getUsersInRoom', function(data) {
var usersInRoom = [];
var socketsInRoom = io.sockets.clients(data.room);
for (var i=0; i<socketsInRoom.length; i++) {
db.hgetall(socketsInRoom[i].id, function(err, obj) {
usersInRoom.push({'room':data.room, 'username':obj.username, 'id':obj.socketID});
// When we've finished with the last one, notify user
if (usersInRoom.length == socketsInRoom.length) {
socket.emit('usersInRoom', {'users':usersInRoom});
}
});
}
});

// User wants to change his nickname
socket.on('setNickname', function(data) {
// Get user info from db
db.hget([socket.id, 'username'], function(err, username) {

// Store user data in db
db.hset([socket.id, 'username', data.username], redis.print);
logger.emit('newEvent', 'userSetsNickname', {'socket':socket.id, 'oldUsername':username, 'newUsername':data.username});

// Notify all users who belong to the same rooms that this one
_.each(_.keys(io.sockets.manager.roomClients[socket.id]), function(room) {
room = room.substr(1); // Forward slash before room name (socket.io)
if (room) {
var info = {'room':room, 'oldUsername':username, 'newUsername':data.username, 'id':socket.id};
io.sockets.in(room).emit('userNicknameUpdated', info);
}
});
});
});

// New message sent to group
socket.on('newMessage', function(data) {
db.hgetall(socket.id, function(err, obj) {
if (err) return logger.emit('newEvent', 'error', err);

// Check if user is subscribed to room before sending his message
if (_.has(io.sockets.manager.roomClients[socket.id], "/"+data.room)) {
var message = {'room':data.room, 'username':obj.username, 'msg':data.msg, 'date':new Date()};
// Send message to room
io.sockets.in(data.room).emit('newMessage', message);
logger.emit('newEvent', 'newMessage', message);
}
});
});

// Clean up on disconnect
socket.on('disconnect', function() {

// Get current rooms of user
var rooms = _.clone(io.sockets.manager.roomClients[socket.id]);

// Get user info from db
db.hgetall(socket.id, function(err, obj) {
if (err) return logger.emit('newEvent', 'error', err);
logger.emit('newEvent', 'userDisconnected', {'socket':socket.id, 'username':obj.username});

// Notify all users who belong to the same rooms that this one
_.each(_.keys(rooms), function(room) {
room = room.substr(1); // Forward slash before room name (socket.io)
if (room) {
var message = {'room':room, 'username':obj.username, 'msg':'----- Left the room -----', 'id':obj.socketID};
io.sockets.in(room).emit('userLeavesRoom', message);
}
});
});

// Delete user from db
db.del(socket.id, redis.print);
});
});

// Automatic message generation (for testing purposes)
if (conf.debug) {
setInterval(function() {
var text = 'Testing rooms';
sendBroadcast(text);
}, 60000);
}

14 changes: 14 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "simplechat",
"author": "Sergio Castaño Arteaga - [email protected]",
"description": "SimpleChat",
"version": "0.0.1",
"private": true,
"dependencies": {
"express": "3.x",
"socket.io": "",
"underscore": "",
"redis": "",
"validator": ""
}
}
16 changes: 16 additions & 0 deletions static/chat.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost:8888');
socket.on('connected', function (data) {
console.log(data);
socket.emit('subscribe', {'username':'tegioz', 'rooms':['pruebas']});
socket.emit('newMessage', {'room':'pruebas', 'msg':'Holaaaaaa'});
});
socket.on('newMessage', function (data) {
console.log("newMessage: %s", JSON.stringify(data));
});
setInterval(function() {
socket.emit('unsubscribe', {'rooms':['pruebas']});
socket.disconnect();
}, 600000);
</script>
Loading

0 comments on commit 1e15a0e

Please sign in to comment.