📜 ⬆️ ⬇️

Advanced chat on Node.JS

Yes, the Internet is full of implementations of a banal chat, but still I don’t like them. I present to you my chat implementation, with blackjack and you know what.

So, immediately link to the demo for the impatient.
(The server has already been laid)

Special features




')

How does it work



Well, everything is simple


How to do this



Installing dependencies


sudo yum install nodejs sudo yum install mongodb npm install ws npm install mongodb 


We program


To begin with we will make client part while node.js is established and all the rest necessary for server.

HTML is very laconic

 <!DOCTYPE html> <html> <head> <link href='http://fonts.googleapis.com/css?family=Ubuntu&subset=latin,cyrillic' rel='stylesheet' type='text/css'> <link href="main.css" rel="stylesheet" /> <script src="main.js" defer></script> <meta charset="UTF-8"> </head> <body> <form id="loginform" class="unauthorized"> <input id="login" placeholder=""><br> <input id="password" placeholder=""> <div>*    ,   </div> </form> <output id="messages"></output> <div> <div contenteditable id="input"></div> </div> </body> </html> 


Thanks to the defer attribute of the script tag, javascript will start only after loading the entire page. This is much more convenient than the window.onload event.

To reduce the code, reduce the document.getElementById to $

 function $(a){return document.getElementById(a)} 


Open the connection to the server and wait for incoming messages

 ws = new WebSocket ('ws://x.cloudx.cx:9000'); ws.onmessage = function (message) { //        var event = JSON.parse(message.data); //     ,   switch (event.type) { case 'message': //    var name = document.createElement('div'); var icon = document.createElement('div'); var body = document.createElement('div'); var root = document.createElement('div'); name.innerText = event.from; body.innerText = specials_in(event); root.appendChild(name); root.appendChild(icon); root.appendChild(body); $('messages').appendChild (root); break; case 'authorize': //      if (event.success) { $('loginform').classList.remove('unauthorized'); } break; default: //   ,       console.log ('unknown event:', event) break; } } 


You may have noticed that incoming messages are first processed by the specials_in function. This function searches for special fragments in the message text that extend the chat functionality.

 function specials_in (event) { var message = event.message; var moment = new Date(event.time); //      var time = (moment.getHours()<10)? '0'+moment.getHours() : moment.getHours(); time = (moment.getMinutes()<10)? time+':0'+moment.getMinutes() : time+':'+moment.getMinutes(); time = (moment.getSeconds()<10)? time+':0'+moment.getSeconds() : time+':'+moment.getSeconds(); var date = (moment.getDate()<10)? '0'+moment.getDate() : moment.getDate(); date = (moment.getMonth()<10)? date+'.0'+moment.getMinutes()+'.'+moment.getFullYear() : date+':'+moment.getMonth()+'.'+moment.getFullYear() message = message.replace(/\[time\]/gim, time); message = message.replace(/\[date\]/gim, date); return message; } 


Similar function for outgoing messages

 function specials_out(message) { // /me message = message.replace(/\s*\/me\s/, $('login').value+' '); return message; } 


The rest of the client code, nothing unusual
 //   Enter     $('password').onkeydown = function (e) { if (e.which == 13) { //    authorize ws.send (JSON.stringify ({ type: 'authorize', user: $('login').value, password: $('password').value })); } } //   Enter     $('input').onkeydown = function (e) { //    Ctrl+Enter  Shift+Enter,     . if (e.which == 13 && !e.ctrlKey && !e.shiftKey) { //    message ws.send (JSON.stringify ({ type: 'message', message: specials_out($('input').innerText) })); $('input').innerText = ''; //    } } //      var observer = new MutationObserver(function(mutations) { mutations.forEach(function(mutation) { var objDiv = $('messages'); objDiv.scrollTop = objDiv.scrollHeight; }); }).observe($('messages'), { childList: true }); 



Now we will start server part

Connecting to the database and waiting for the connection via the 9000 port web socket

 //   var WebSocketServer = require('ws').Server, wss = new WebSocketServer({port: 9000}); //    var MongoClient = require('mongodb').MongoClient, format = require('util').format; var userListDB, chatDB; //    MongoClient.connect('mongodb://127.0.0.1:27017', function (err, db) { if (err) {throw err} //     ()    userListDB = db.collection('users'); chatDB = db.collection('chat'); }); 


Authorization and registration are made as easy as possible for the user. If there is no account, it will be created.

 //         function existUser (user, callback) { userListDB.find({login: user}).toArray(function (error, list) { callback (list.length !== 0); }); } //         function checkUser (user, password, callback) { // ,     existUser(user, function (exist) { //    if (exist) { //        userListDB.find({login: user}).toArray(function (error, list) { //   callback (list.pop().password === password); }); } else { //   ,    userListDB.insert ({login: user, password: password}, {w:1}, function (err) { if (err) {throw err} }); //   ,   callback (true); } }); } 


Sending a message to all chat participants
For this function to work, links to connections with each member are in the peers array.

 //     function broadcast (by, message) { //   ,     var time = new Date().getTime(); //     peers.forEach (function (ws) { ws.send (JSON.stringify ({ type: 'message', message: message, from: by, time: time })); }); //     chatDB.insert ({message: message, from: by, time: time}, {w:1}, function (err) { if (err) {throw err} }); } 


Handling new connections and messages

 //    wss.on('connection', function (ws) { //   var login = ''; var registered = false; //    ws.on('message', function (message) { //      var event = JSON.parse(message); //    ,    if (event.type === 'authorize') { //   checkUser(event.user, event.password, function (success) { //        registered = success; //    var returning = {type:'authorize', success: success}; //  ,  if (success) { //        returning.online = lpeers; //        lpeers.push (event.user); //        peers.push (ws); //        login = event.user; //    ws.on ('close', function () { peers.exterminate(ws); lpeers.exterminate(login); }); } //  , ,   ws.send (JSON.stringify(returning)); //      if (success) { sendNewMessages(ws); } }); } else { //    ,    if (registered) { //    switch (event.type) { //    case 'message': //    broadcast (login, event.message) break; //    ,     case 'type': //     ,      break; } } } }); }); 


For the operation of the code above, you also need the function of receiving messages from the history, a list of people online and the function of removing an element from the array

 //    ( ) var lpeers = []; //        function sendNewMessages (ws) { chatDB.find().toArray(function(error, entries) { if (error) {throw error} entries.forEach(function (entry){ entry.type = 'message'; ws.send (JSON.stringify (entry)); }); }); } //        Array.prototype.exterminate = function (value) { this.splice(this.indexOf(value), 1); } 


Chat is ready!

Sources can be taken here
(Server lies regularly)

Can run

 su - mongod --smallfiles > /dev/null & node path/to/server.js > /dev/null & 


Todo


Source: https://habr.com/ru/post/200866/


All Articles