Chat în timp real cu Node.js Readline & Socket.io

Ce veți crea

Node.js are un modul sub-apreciat în biblioteca standard, care este surprinzător de util. Modulul Readline face ceea ce se spune în cutie: citește o linie de intrare de la terminal. Acest lucru poate fi folosit pentru a cere utilizatorului o întrebare sau două sau pentru a crea un prompt în partea de jos a ecranului. În acest tutorial, intenționez să arăt capacitatea Readline și să fac o cameră de chat CLI în timp real susținută de Socket.io. Clientul nu va trimite doar mesaje simple, ci va avea comenzi pentru emotes cu /pe mine, mesaje private cu / msg, și să permită schimbarea poreclelor cu / nick.

Un pic despre Readline

Aceasta este probabil cea mai simplă utilizare a Readline:

var readline = necesită ('readline'); var rl = readline.createInterface (process.stdin, process.stdout); rl.question ("Care este numele dvs.?", funcția (răspunsul) console.log ("Hello", + answer); rl.close (););

Includem modulul, creăm interfața Readline cu fluxurile standard de intrare și ieșire, apoi adresăm utilizatorului o întrebare unică. Aceasta este prima utilizare a Readline: punerea de întrebări. Dacă trebuie să confirmați ceva cu un utilizator, probabil în forma popularului vreodată, "Vreți să faceți acest lucru (y / n)", care pătrunde în instrumentele CLI, readline.question () este calea de a face acest lucru.

Cealaltă funcționalitate pe care Readline o oferă este promptul, care poate fi personalizat din setarea implicită ">"și temporar întrerupt pentru a împiedica accesul. Pentru clientul nostru de chat Readline, aceasta va fi interfața noastră principală. readline.question () pentru a cere utilizatorului o porecla, dar orice altceva va fi readline.prompt ().

Gestionarea dependențelor

Să începem cu partea plictisitoare: dependențele. Acest proiect va face uz de socket.io, socket.io-client pachet și ansi-culoare. Ta packages.json fișier ar trebui să arate ceva de genul:

"nume": "ReadlineChatExample", "versiunea": "1.0.0", "descriere": "Chat CLI cu readline și socket.io", "autor": "Matt Harzewski" . "" ultima "," socket.io-client ":" cea mai recentă "," ansi-culoare ":" cea mai recentă "," privată "

Alerga npm install și ar trebui să fiți bine să mergeți.

Server-ul

Pentru acest tutorial, vom folosi un server Socket.io incredibil de simplu. Nu mai are nimic de bază decât acesta:

var socketio = cer ('socket.io'); // Ascultați pe portul 3636 var io = socketio.listen (3636); io.sockets.on ('conexiune', functie (socket) // transmite mesajul unui utilizator tuturor celor din camera socket.on ('send', function (data) io.sockets.emit (' date); ); );

Tot ce face este să ia un mesaj primit de la un client și să îl transmită tuturor celorlalți. Serverul ar fi probabil mai robust pentru o aplicație pe scară largă, dar pentru acest exemplu simplu ar trebui să fie suficient.

Acest lucru ar trebui salvat în directorul de proiecte ca server.js.

Clientul: Include & Configurare

Înainte de a ajunge la partea distractivă, trebuie să includem dependențele noastre, să definim câteva variabile și să pornim interfața Readline și conexiunea socket.

var readline = necesită ('readline'), socketio = necesită ('socket.io-client'), util = require ('util'), color = var nick; var socket = socketio.connect ('localhost', port: 3636); var rl = readline.createInterface (process.stdin, process.stdout);

Codul este destul de explicabil în acest moment. Avem variabila de pseudonim, conexiunea socket (prin socket.io-client pachet) și interfața Readline.

Socket.io se va conecta la localhost over port 3636 în acest exemplu, desigur, acest lucru ar fi schimbat în domeniul și portul serverului dvs., dacă efectuați o aplicație de chat de producție. (Nu mai are sens să vorbești cu tine!)

Clientul: Solicitarea numelui utilizatorului

Acum pentru prima noastră utilizare a Readline! Vrem să îi întrebăm pe utilizator pentru alegerea poreclei, care îi va identifica în chat. Pentru aceasta, vom folosi Readline's întrebare() metodă.

// Setați numele de utilizator rl.question ("Vă rugăm să introduceți un pseudonim:", funcția (nume) nick = nume; var msg = nick + "a intrat în chat"; socket.emit (' notă ", mesaj: msg); rl.prompt (true););

Am setat variabila nick de la înainte, la valoarea colectată de la utilizator, trimitem un mesaj către server (care va fi transmis către ceilalți clienți) pe care utilizatorul nostru sa alăturat conversației, apoi trece interfața Readline în modul prompt. Adevărat valoarea a trecut la prompt() se asigură că caracterul prompt este afișat corespunzător. (În caz contrar, cursorul se poate deplasa în poziția zero de pe linie și ">"nu va fi afișat.)

Din păcate, Readline are o problemă frustrantă cu prompt() metodă. Nu se joacă frumos cu console.log (), care va scoate textul pe aceeași linie ca și caracterul prompt, lăsându-l pe ">"personaje de pretutindeni și alte ciudățenie. Pentru a remedia acest lucru, nu vom folosi console.log oriunde în această aplicație, cu excepția unui singur loc. În schimb, ieșirea trebuie transferată la această funcție:

funcția console_out (msg) process.stdout.clearLine (); process.stdout.cursorTo (0); console.log (msg); rl.prompt (true); 

Acest puțin soluția hacky asigură că linia curentă din consola este goală și că cursorul se află în poziția zero înainte de a imprima ieșirea. Apoi, în mod explicit, solicită ca promptul să fie din nou afișat, după aceea.

Deci, pentru restul acestui tutorial, veți vedea console_out () in loc de console.log ().

Clientul: manipularea intrării

Există două tipuri de intrări pe care un utilizator le poate introduce: chat și comenzi. Știm că comenzile sunt precedate de un slash, astfel încât este ușor să distingem între cele două.

Readline are mai mulți agenți de procesare a evenimentelor, dar cea mai importantă este fără îndoială linia. Ori de câte ori este detectat un caracter de linie nouă în fluxul de intrare (de la tasta return sau enter), acest eveniment se declanșează. Așa că trebuie să ne căutăm linia pentru managerul nostru de intrare.

rl.on ('line', funcție (linie) if (line [0] == "/" && line.length> 1) var cmd = line.match (/ [az] + \ b / ]; altul // trimite mesajul de chat socket.emit ('trimite', type: 'chat', 'chat' mesaj: linie, nick: nick); rl.prompt (adevărat););

Dacă primul caracter al liniei de intrare este o slash, știm că este o comandă, care va necesita mai mult procesare. În caz contrar, trimitem doar un mesaj de chat obișnuit și resetați promptul. Observați diferența dintre datele trimise prin socket aici și pentru mesajul de conectare din pasul anterior. Folosește un altul tip, astfel încât clientul primitor știe cum să formateze mesajul și îl transmitem nick variabilă, de asemenea.

Numele comenzii (cmd) și textul care urmează (arg) sunt izolate cu un pic de regex și magie substring, apoi le transmitem unei funcții care procesează comanda.

funcția chat_command (cmd, arg) comutare (cmd) case 'nick': var notă = nick + "a schimbat numele în" + arg; nick = arg; socket.emit ('trimite', type: 'notice', mesaj: notice); pauză; caz "msg": var la = arg.match (/ [a-z] + \ b /) [0]; var mesaj = arg.substr (la.length, arg.length); socket.emit ('trimite', tip: 'spune', mesaj: mesaj, la: la, de la: nick); pauză; cazul "me": var emote = nick + "" + arg; socket.emit ('trimite', type: 'emote', mesaj: emote); pauză; implicit: console_out ("Aceasta nu este o comandă validă"); 

În cazul în care tipul de utilizator / nick gollum, nick variabila este resetată să fie Gollum, unde ar fi putut fi Smeagol înainte și o notificare este împins la server.

În cazul în care tipul de utilizator / msg bilbo Unde este prețiosul?, același regex este folosit pentru a separa destinatarul și mesajul, apoi un obiect cu tipul de spune este împins către server. Acesta va fi afișat un pic diferit de un mesaj normal și nu ar trebui să fie vizibil pentru alți utilizatori. Desigur, serverul nostru prea simplu va împinge orbește mesajul către toată lumea, dar clientul va ignora spune că nu este adresat pseudonimului corect. Un server mai robust ar putea fi mai discret.

Comanda emote este folosită sub forma lui / Eu mănânc un mic dejun. Porecla este prefixată la emote într-un mod care ar trebui să fie familiar pentru oricine care a folosit IRC sau a jucat un joc de jocuri de noroc multiplayer, apoi este împins către server.

Clientul: Manipularea mesajelor primite

Acum clientul are nevoie de o modalitate de a primi mesaje. Tot ce trebuie să facem este să intrăm în clientul Socket.io mesaj eveniment și să formateze datele corespunzător pentru ieșire.

socket.on ("mesaj", functie (data) var leader; daca (data.type == 'chat' && data.nick! = nick) leader =<"+data.nick+"> "," verde "); console_out (leader + data.message); altfel dacă (date.type ==" notă ") console_out (culoare (data.message, cyan); tip == "spune" && data.to == nick) leader = culoare ("[" + data.from + "->" + data.to + "]", "roșu"); ); altfel dacă (data.type == "emote") console_out (culoare (data.message, "cyan")););

Mesajele cu un tip de conversație acea nu au fost trimise de client folosind porecla noastră sunt afișate cu porecla și textul de chat. Utilizatorul poate vedea deja ceea ce au introdus în Readline, deci nu are rost să-l scoateți din nou. Aici folosesc ansi-culoare pachet pentru a colora iesirea putin. Nu este strict necesar, dar face mai ușor de urmat chat-ul.

Mesajele cu un tip de înștiințare sau emote sunt tipărite așa cum este, deși sunt colorate cu cianuri.

Dacă mesajul este a spune și porecla este egală cu numele curent al acestui client, ieșirea are forma de [Cineva-> tu] Bună!. Desigur, acest lucru nu este deloc privat. Dacă ați vrut să vedeți toată lumea e mesaje, tot ce trebuie să faceți este să scoateți && data.to == nick parte. În mod ideal, serverul ar trebui să știe pe cine să trimită mesajul și să nu îl trimită clienților care nu au nevoie de el. Dar asta adaugă complexitate inutilă, care depășește sfera acestui tutorial.

Da-i foc!

Acum, să vedem dacă funcționează toate. Pentru a le testa, lansați serverul rulând nod server.js și apoi deschideți câteva ferestre terminale noi. În ferestrele noi, rulați nod client.js și introduceți un pseudonim. Ar trebui să puteți discuta între ele, presupunând că totul merge bine.

.

Cod