Ssh Chat

Hallo Habr. Konsolen-Chat ist eine großartige Sache, aber für das Front-End, was ist, wenn Sie dasselbe wollen, aber für das Backend. Wenn ja, dann ist dieser Artikel für Sie. Aber welches Tool wird im Backend oft verwendet? Das ist richtig, ssh, also vertrete ich sshchat.


Wie wird es aussehen?


Irgendwo auf dem Server dreht sich das Programm auf dem Knoten.
Sobald jemand eine Verbindung zum Chat herstellen möchte, gibt er Folgendes ein:


ssh server -p 8022 

Danach fragt das System nach einem Passwort und überprüft es mit dem Passwort in einer speziellen Datei. Wenn das Passwort übereinstimmt, stellen wir eine Verbindung zum Chat her (der Benutzer erhält 100 vorherige Nachrichten und alle anderen sehen, dass er verbunden ist).


Dann empfängt er die Nachrichten anderer und kann seine eigenen schreiben.


Hier mit interessanteren Nachrichten:


 @box{@color(red){Red text in box}} 

Senden Sie roten Text in Feld.


Fangen wir an


Um mit ssh zu arbeiten, verwenden wir https://www.npmjs.com/package/ssh2 .
Für die Formatierung verwenden wir Kreide und Boxen.
Also installiere sie:


 npm i ssh2 chalk boxen 

Jetzt ist der Code selbst einer der wichtigsten Teile dieses Nachrichtenparsers ( GitHub ):


 //  chalk  boxen const chalk = require('chalk'); const boxen = require('boxen'); //         @ //   2           let methods = { color: function(args, text) { return chalk.keyword(args)(text); }, bold: function(args, text) { return chalk.bold(text); }, underline: function(args, text) { return chalk.underline(text); }, hex: function(args, text) { return chalk.hex(args)(text); }, box: function(args, text) { return boxen(text, { borderStyle: 'round', padding: 1, borderColor: 'blueBright' }); } }; //   function parseAndExecute(str) { let pos = 0; let stage = 0; let nS = ''; let bufs = ['', '', '', '']; let level = 0; while (pos < str.length) { let symbol = str[pos]; pos++; if (symbol == '\\' && '(){}@'.indexOf(str[pos]) !== -1) { bufs[stage] += str[pos]; pos++; continue; } if (stage == 0 && symbol == '@') { stage++; nS += bufs[0]; bufs[0] = ''; continue; } else if (stage >= 1) { if (symbol == '(') if (stage < 2) { stage = 2; } else { level++; } if (symbol == ')' && stage >= 2 && level > 0) level--; if (symbol == '{') if (stage != 3) { stage = 3; } else { level++; } if (symbol == '}') { if (level == 0) { bufs[3] += '}'; nS += methods[bufs[1]](bufs[2].slice(1, -1), parseAndExecute(bufs[3].slice(1, -1))); bufs = ['', '', '', '']; stage = 0; continue; } else { level--; } } } bufs[stage] += symbol; } return nS + bufs[0]; } module.exports.parseAndExecute = parseAndExecute; 

Formatierung ( GitHub ):


 const chalk = require('chalk'); const { parseAndExecute } = require('./parserExec') //  (    ) function getNick(nick) { let hash = 0; for (var i = 0; i < nick.length; i++) hash += nick.charCodeAt(i) - 32; return chalk.hsv((hash + 160) % 360, 90, 90)(chalk.bold(nick)); } module.exports.format = function(nick, message) { const nickSpace = '\r ' + ' '.repeat(nick.length); nick = getNick(nick) + ': '; message = message.replace(/\\n/gm, '\n'); //  \n   message = parseAndExecute(message) //  //       message = message .split('\n') .map((e, i) => '' + (i !== 0 ? nickSpace : '') + e) .join('\n'); return nick + message; }; 

Methoden zum Senden einer Nachricht an alle Benutzer und zum Speichern von 100 Nachrichten ( GitHub ):


 let listeners = []; //   let cache = new Array(100).fill('') //  //     module.exports.addListener = write => listeners.push(write) - 1; module.exports.delListener = id => listeners.splice(id, 1); //   module.exports.broadcast = msg => { cache.shift() cache.push(msg) process.stdout.write(msg) listeners.forEach(wr => wr(msg)); } //   module.exports.getCache = ()=>cache.join('\r\033[1K') 

Lobby, Servererstellung und Autorisierung ( GitHub ):


 const { Server } = require('ssh2'); const { readFileSync } = require('fs'); const hostKey = readFileSync('./ssh'); //   const users = JSON.parse(readFileSync('./users.json')); //  let connectionCallback = () => {}; module.exports.createServer = function createServer({ lobby }) { //   const server = new Server( { banner: lobby, //      hostKeys: [hostKey] }, function(client) { nick = ''; client .on('authentication', ctx => { //  if (ctx.method !== 'password') return ctx.reject(); if (ctx.password !== users[ctx.username]) ctx.reject(); nick = ctx.username; ctx.accept(); }) .on('ready', function() { connectionCallback(client, nick); }); } ); return server }; module.exports.setConnectCallback = callback => { //     connectionCallback = callback; }; 

Verschiedene Methoden ( GitHub ):


 const { createInterface } = require('readline'); module.exports.getStream = function(client, onStream, onEnd){ client //     .on('session', function(accept, reject) { accept() .on('pty', accept => accept & accept()) .on('shell', accept => onStream(accept())); }) .on('end', () => onEnd()); } //   module.exports.getCommunicator = function(stream, onMessage, onEnd){ let readline = createInterface({ //     input: stream, output: stream, prompt: '> ', historySize: 0, terminal: true }) readline.prompt() readline.on('close', ()=>{ radline = null; onEnd() stream.end() }) readline.on('line', (msg)=>{ stream.write('\033[s\033[1A\033[1K\r') onMessage(msg) readline.prompt() }) //     return msg=>{ stream.write('\033[1K\r' + msg) readline.prompt() } } 

Jetzt kombiniere ( GitHub ):


 const { createServer, setConnectCallback } = require('./lobby'); const { getStream, getCommunicator } = require('./utils'); const { addListener, delListener, broadcast, getCache } = require('./broadcaster'); const { format, getNick } = require('./format'); //    module.exports = function({ lobby = 'Hi' } = {}) { const server = createServer({ lobby }); setConnectCallback((client, nick) => { //   console.log('Client authenticated!'); let id = null; getStream( //   client, stream => { const write = getCommunicator( //   stream, msg => { if (msg == '') return; try { broadcast(format(nick, msg) + '\n'); //    ,    } catch (e) {} }, () => {} ); id = addListener(write); //   write('\033c' + getCache()); //   broadcast(getNick(nick) + ' connected\n'); //    }, () => { delListener(id); broadcast(getNick(nick) + ' disconnected\n') //    } ); }); server.listen(8022); }; 

Und der letzte Schritt ist ein Beispielserver:


 const chat = require('.') chat({}) 

Die Datei users.json beschreibt auch Benutzer und ihre Kennwörter.


Schlussfolgerungen


So kann man nicht den einfachsten Chat in ssh schreiben.
Für einen solchen Chat muss der Client nicht schreiben, er verfügt über die Entwurfsfunktionen und jeder kann ihn bereitstellen.


Was kann man noch tun:


  • Fügen Sie die Möglichkeit hinzu, eigene Designmerkmale zu erstellen
  • Markdown-Unterstützung hinzufügen
  • Bot-Unterstützung hinzufügen
  • Erhöhen Sie die Passwortsicherheit (Hash und Salt)

Endgültiges Repository

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


All Articles