Real-time chat med Node.js 'Readline & Socket.io

Hva du skal skape

Node.js har en underforstått modul i sitt standardbibliotek som er overraskende nyttig. Leselinjemodulen gjør det som står på boksen: det leser en linje med inngang fra terminalen. Dette kan brukes til å spørre brukeren et spørsmål eller to, eller å opprette en melding nederst på skjermen. I denne opplæringen har jeg tenkt å vise muligheten til Readline og lage et sanntids CLI-chatrom støttet av Socket.io. Klienten vil ikke bare sende enkle meldinger, men har kommandoer for følelser med /meg, private meldinger med / msg, og la det være mulig å endre kallenavn med / nick.

Litt om Readline

Dette er trolig den enkleste bruken av Readline:

var readline = krever ('readline'); var rl = readline.createInterface (process.stdin, process.stdout); rl.question ("Hva er navnet ditt?", funksjon (svar) console.log ("Hei," + svar); rl.close (););

Vi inkluderer modulen, opprett Readline-grensesnittet med standardinngangs- og utgangsstrømmene, og spør brukeren om et enkelt spørsmål. Dette er den første bruken av Readline: stille spørsmål. Hvis du trenger å bekrefte noe med en bruker, kanskje i form av den stadig populære, "Vil du gjøre dette? (Y / n)", som gjennomsyrer CLI-verktøy, readline.question () er måten å gjøre det på.

Den andre funksjonaliteten som Readline gir, er spørringen, som kan tilpasses fra standardinnstillingen ">"tegn og midlertidig midlertidig stoppet for å hindre innspill. For vår Readline-chat-klient vil dette være vårt primære grensesnitt. Det vil være en enkelt forekomst av readline.question () å spørre brukeren om et kallenavn, men alt annet vil være readline.prompt ().

Administrere dine avhengigheter

La oss starte med kjedelig del: avhengigheter. Dette prosjektet vil gjøre bruk av socket.io, de socket.io-klient pakke og ansi-farge. Din packages.json filen skal se slik ut:

"navn": "ReadlineChatExample", "versjon": "1.0.0", "beskrivelse": "CLI chat med readline and socket.io", "author": "Matt Harzewski", "avhengigheter" .io ":" nyeste "," socket.io-client ":" nyeste "," ansi-farge ":" nyeste "," private "

Løpe npm installasjon og du bør være god å gå.

Serveren

For denne opplæringen bruker vi en utrolig enkel Socket.io-server. Det blir ikke noe mer grunnleggende enn dette:

var socketio = krever ('socket.io'); // Hør på port 3636 var io = socketio.listen (3636); io.sockets.on ('tilkobling', funksjon (socket) // Sende en brukermelding til alle andre i rom socket.on ('send', funksjon (data) io.sockets.emit ('message' data);););

Alt det gjør er å ta imot en innkommende melding fra en klient og sende den videre til alle andre. Serveren vil trolig være mer robust for en større applikasjon, men for dette enkle eksempelet skal det være tilstrekkelig.

Dette bør lagres i prosjektkatalogen som server.js.

Klienten: Inkluderer og oppsett

Før vi kommer til den morsomme delen, må vi inkludere våre avhengigheter, definere noen variabler og starte Readline-grensesnittet og kontakten.

var readline = krever ('readline'), socketio = krever ('socket.io-client'), util = krever ('util'), farge = krever ("ansi-farge"). var nick; var socket = socketio.connect ('localhost', port: 3636); var rl = readline.createInterface (process.stdin, process.stdout);

Koden er ganske mye selvforklarende på dette punktet. Vi har vår kallenavn variabel, stikkontakten (gjennom socket.io-klient pakke) og vårt Readline-grensesnitt.

Socket.io vil koble til localhost over port 3636 i dette eksemplet vil selvfølgelig dette bli endret til din egen server domenet og porten, hvis du lager en produksjon chat app. (Det er ikke mye poeng i å chatte med deg selv!)

Klienten: Be om brukerens navn

Nå for vår første bruk av Readline! Vi ønsker å spørre brukeren for deres valg av kallenavn, som vil identifisere dem i chatterommet. For dette vil vi bruke Readlines spørsmål() metode.

// Angi brukernavnet rl.question ("Vennligst skriv inn et kallenavn:", funksjon (navn) nick = navn; var msg = nick + "har sluttet seg til chatten"; socket.emit ('send', type: ' legge merke til ', melding: msg); rl.prompt (true););

Vi stiller inn nøkkelvariabelen fra før, til verdien som er samlet fra brukeren, send en melding til serveren (som blir viderekoblet til de andre klientene) at brukeren vår har sluttet seg til chatten, og deretter bytt Leslinje-grensesnittet tilbake til hurtigmodus. De ekte verdi overført til prompt () sørger for at meldingstegnet vises riktig. (Ellers kan markøren bevege seg til nullposisjon på linjen og ">"vil ikke bli vist.)

Dessverre har Readline et frustrerende problem med prompt () metode. Det spiller ikke fint med console.log (), som vil skrive ut tekst på samme linje som den snakkede karakteren, og etterlate seg ">"tegn overalt og annen underlighet. For å rette opp dette, vil vi ikke bruke console.log hvor som helst i denne applikasjonen, lagre for ett sted. I stedet skal produksjonen sendes til denne funksjonen:

funksjonskonsoll_out (msg) process.stdout.clearLine (); process.stdout.cursorTo (0); console.log (msg); rl.prompt (true); 

Dette litt hacky løsning sikrer at den nåværende linjen i konsollen er tom, og at markøren er i nullstilling før utskrift av utgangen. Deretter krever det eksplisitt at meldingen blir sendt ut igjen, etterpå.

Så for resten av denne opplæringen, vil du se console_out () i stedet for console.log ().

Klienten: Handling Input

Det finnes to typer innspill som en bruker kan legge inn: chat og kommandoer. Vi vet at kommandoer er foran et skråstrek, så det er enkelt å skille mellom de to.

Readline har flere arrangementshåndterere, men det viktigste er utvilsomt linje. Når en newline-karakter oppdages i inngangsstrømmen (fra retur- eller enter-tasten), bryr denne hendelsen. Så vi må koble inn linje for vår inngangshåndterer.

rl.on ('line', funksjon (linje) hvis (linje [0] == "/" && line.length> 1) var cmd = line.match (/ [az] + \ b /) [0 ]; var arg = line.substr (cmd.length + 2, line.length); chat_command (cmd, arg); else // send chat message socket.emit ('send', type: 'chat' melding: linje, nick: nick); rl.prompt (sant););

Hvis det første tegn på inngangslinjen er et skråstrek, vet vi at det er en kommando, som krever mer behandling. Ellers sender vi bare en vanlig chatmelding og tilbakestiller spørringen. Merk forskjellen mellom dataene som sendes over stikkontakten her og for meldingen i det forrige trinnet. Det bruker en annen type, slik at mottakerklienten vet hvordan du formaterer meldingen, og vi overfører nick variabel også.

Kommandoenavnet (cmd) og teksten som følger (arg) er isolert med litt regex og substring magi, så sender vi dem til en funksjon som behandler kommandoen.

funksjon chat_command (cmd, arg) switch (cmd) case 'nick': var notice = nick + "endret navn til" + arg; nick = arg; socket.emit ('send', type: 'varsel', melding: varsel); gå i stykker; case 'msg': var til = arg.match (/ [a-z] + \ b /) [0]; var message = arg.substr (to.length, arg.length); socket.emit ('send', type: 'tell', melding: melding, til: til, fra: nick); gå i stykker; sak 'meg': var emote = nick + "" + arg; socket.emit ('send', type: 'emote', melding: emote); gå i stykker; standard: console_out ("Det er ikke en gyldig kommando."); 

Hvis brukeren skriver / nick gollum, de nick variabel er tilbakestilt til å være Gollum, hvor det kunne vært Smeagol før og et varsel blir presset til serveren.

Hvis brukeren skriver / msg bilbo Hvor er den dyrebare?, Den samme regex brukes til å skille mottakeren og meldingen, deretter et objekt med typen fortelle blir presset til serveren. Dette vil bli vist litt annerledes enn en vanlig melding og bør ikke være synlig for andre brukere. Helt klart vil vår overdrivet enkle server blinke push meldingen ut til alle, men klienten vil ignorere forteller at det ikke er adressert til riktig kallenavn. En mer robust server kan være mer diskret.

Emote kommandoen brukes i form av / jeg spiser andre frokost. Kallenavnet er prepended til emote på en måte som burde være kjent for alle som har brukt IRC eller spilt et multiplayer rollespill, så blir det presset til serveren.

Klienten: Håndtering Innkommende meldinger

Nå trenger kunden en måte å motta meldinger på. Alt vi trenger å gjøre er å koble til Socket.io klientens budskap hendelse og format dataene riktig for utdata.

socket.on ('message', funksjon (data) var leader; if (data.type == 'chat' && data.nick! = nick) leader = color<"+data.nick+"> "," grønn "); console_out (leder + data.message); annet hvis (data.type ==" notice ") console_out (farge (data.message, 'cyan')) annet hvis type == "tell" && data.to == nick) leder = farge ("[" + data.from + "->" + data.to + "]", "rødt"); console_out (leder + data.message ); else if (data.type == "emote") console_out (farge (data.message, "cyan")););

Meldinger med en type chatte at var ikke sendt av klienten ved hjelp av vårt kallenavn, vises med kallenavnet og chat-teksten. Brukeren kan allerede se hva de skrev inn i Leslinjen, så det er ikke noe poeng i å skrive det ut igjen. Her bruker jeg ansi-farge pakke for å fargelegge produksjonen litt. Det er ikke strengt nødvendig, men det gjør chatten enklere å følge.

Meldinger med en type legge merke til eller emote er trykt som-er, men farget cyan.

Hvis meldingen er a fortelle og kallenavnet er lik denne klientens nåværende navn, utgangen tar form av [Noen-> Du] Hei!. Selvfølgelig er dette ikke veldig privat. Hvis du ønsket å se alles meldinger, alt du trenger å gjøre er å ta ut && data.to == nick del. Ideelt sett bør serveren vite hvilken klient som skal presse meldingen til og ikke sende den til klienter som ikke trenger det. Men det gir unødvendig kompleksitet som ligger utenfor omfanget av denne opplæringen.

Brann det opp!

La oss nå se om alt fungerer. For å teste det ut, start serveren ved å kjøre node server.js og åpne deretter et par nye terminalvinduer. I de nye vinduene, løp node client.js og skriv inn et kallenavn. Du bør da kunne chatte mellom dem, forutsatt at alt går bra.

.