Chat ssh

Hola Habr El chat de consola es una gran cosa, pero para el front-end, pero qué pasa si quieres lo mismo, pero para el backend. Si es así, entonces este artículo es para ti. Pero, ¿qué herramienta se usa a menudo en el backend? Así es ssh, así que represento sshchat.


Como se vera


En algún lugar del servidor, el programa en el nodo está girando.
Tan pronto como alguien quiere conectarse al chat, ingresa:


ssh server -p 8022 

Después de eso, el sistema solicita una contraseña y la verifica con la contraseña en un archivo especial. Si la contraseña coincide, nos conectamos al chat (el usuario recibe 100 mensajes anteriores y todos los demás ven que está conectado).


Luego recibe los mensajes de los demás y puede escribir los suyos.


Aquí con mensajes más interesantes:


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

Enviar texto rojo en el cuadro.


Empecemos


Para trabajar con ssh, usaremos https://www.npmjs.com/package/ssh2 .
Para formatear utilizamos tiza y boxen.
Así que instálalos:


 npm i ssh2 chalk boxen 

Ahora el código en sí es una de las partes más importantes de este analizador de mensajes ( 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; 

Formateo ( 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; }; 

Métodos para enviar un mensaje a todos los usuarios y guardar 100 mensajes ( 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, creación y autorización del servidor ( 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; }; 

Varios métodos ( 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() } } 

Ahora combine ( 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); }; 

Y el paso final es un servidor de ejemplo:


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

El archivo users.json también describe a los usuarios y sus contraseñas.


Conclusiones


Así es como no puedes escribir el chat más fácil en ssh.
Para tal chat, el cliente no necesita escribir, tiene las capacidades de diseño y cualquiera puede implementarlo.


¿Qué más se puede hacer?


  • Agregue la capacidad de crear sus propias características de diseño
  • Agregar soporte de rebajas
  • Agregar soporte de bot
  • Aumentar la seguridad de la contraseña (hash y salt)

Repositorio final

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


All Articles