forked from snxraven/sshChat-CLI
296 lines
7.2 KiB
JavaScript
296 lines
7.2 KiB
JavaScript
const blessed = require('neo-blessed');
|
|
// Require the needed libs
|
|
const Hyperswarm = require('hyperswarm')
|
|
const crypto = require('hypercore-crypto')
|
|
const b4a = require('b4a')
|
|
const readline = require('readline')
|
|
const fs = require("fs");
|
|
|
|
// Import our command
|
|
const { login } = require('./commands/login');
|
|
const { execute } = require('./commands/exec');
|
|
const { stop } = require('./commands/stop');
|
|
const { start } = require('./commands/start');
|
|
const { restart } = require('./commands/restart');
|
|
const { stats } = require('./commands/stats');
|
|
const { changeDir } = require('./commands/cd');
|
|
const { AIRequest } = require('./commands/AI');
|
|
|
|
let rand = Math.floor(Math.random() * 99999).toString();
|
|
|
|
let USERPWD = "/"
|
|
let DAPI_KEY
|
|
let LOGGEDIN = false
|
|
let MYKEY = []
|
|
let conns = []
|
|
let connectedUsers = [];
|
|
let USERNAME = ["annon" + rand]
|
|
|
|
function sleep(ms) {
|
|
return new Promise((resolve) => {
|
|
setTimeout(resolve, ms);
|
|
});
|
|
}
|
|
|
|
|
|
function addUser(user, peerId) {
|
|
connectedUsers.push({ name: user, peerId: peerId });
|
|
sidebarBox.setContent(connectedUsers.map(user => `${user.name} - ${user.peerId}`).join("\n"));
|
|
screen.render();
|
|
}
|
|
|
|
function removeUser(peerId) {
|
|
connectedUsers = connectedUsers.filter(user => user.peerId !== peerId);
|
|
sidebarBox.setContent("Connected Peers: \n" + connectedUsers.map(user => `${user.name} - ${user.peerId}`).join("\n"));
|
|
screen.render();
|
|
}
|
|
|
|
// Create the screen
|
|
const screen = blessed.screen({
|
|
smartCSR: true,
|
|
fastCSR: true
|
|
});
|
|
|
|
let mainBox = blessed.box({
|
|
parent: screen,
|
|
top: 0,
|
|
left: 0,
|
|
width: '80%',
|
|
height: '80%',
|
|
border: {
|
|
type: 'line'
|
|
},
|
|
style: {
|
|
fg: 'white',
|
|
bg: 'black'
|
|
},
|
|
keys: true,
|
|
vi: true,
|
|
alwaysScroll: true,
|
|
scrollable: true,
|
|
scrollbar: {
|
|
style: {
|
|
bg: 'yellow'
|
|
}
|
|
}
|
|
});
|
|
|
|
async function updateScroll() {
|
|
mainBox.scrollTo(mainBox.getScrollHeight());
|
|
|
|
}
|
|
|
|
// Create the STDIN box for chat input and command input
|
|
const stdinBox = blessed.textbox({
|
|
bottom: '0',
|
|
left: '0',
|
|
width: '80%',
|
|
height: '21%',
|
|
border: {
|
|
type: 'line'
|
|
},
|
|
style: {
|
|
fg: 'white',
|
|
bg: 'black',
|
|
border: {
|
|
fg: '#f0f0f0'
|
|
},
|
|
},
|
|
inputOnFocus: true,
|
|
input: true
|
|
});
|
|
|
|
// Create the sidebar box for connected peers
|
|
const sidebarBox = blessed.box({
|
|
top: '0',
|
|
right: '0',
|
|
width: '20%',
|
|
height: '100%',
|
|
content: '',
|
|
border: {
|
|
type: 'line'
|
|
},
|
|
style: {
|
|
fg: 'white',
|
|
bg: 'black',
|
|
border: {
|
|
fg: '#f0f0f0'
|
|
},
|
|
}
|
|
});
|
|
|
|
sidebarBox.setLabel("Connected Peers: (Currently None)")
|
|
|
|
|
|
const originalLog = console.log;
|
|
console.log = (...args) => {
|
|
mainBox.setContent(mainBox.getContent() + `\n${args.join(' ')}`);
|
|
updateScroll()
|
|
stdinBox.clearValue();
|
|
|
|
screen.render()
|
|
|
|
}
|
|
|
|
// Generate a random public key
|
|
const publicKey = crypto.randomBytes(32)
|
|
|
|
// Create the swarm and pass in the public key
|
|
const swarm = new Hyperswarm()
|
|
|
|
const commandDir = './commands/';
|
|
const commandFiles = fs.readdirSync(commandDir);
|
|
|
|
const commands = {}
|
|
|
|
for (const file of commandFiles) {
|
|
const commandName = file.split(".")[0]
|
|
require(`${commandDir}/${file}`)
|
|
const command = require(`${commandDir}/${file}`);
|
|
commands[commandName] = command;
|
|
|
|
}
|
|
|
|
async function handleCommand(input) {
|
|
if (input.startsWith("!") || input.startsWith(">")) {
|
|
const command = input.split(" ")
|
|
if (!command) return consoile.log("Please either send a message or enter a command.")
|
|
switch (command[0]) {
|
|
case "!":
|
|
AIRequest(command.slice(1).join(" "))
|
|
break;
|
|
case ">cd":
|
|
USERPWD = await changeDir(command[1], USERPWD);
|
|
console.log(USERPWD)
|
|
break;
|
|
case ">stats":
|
|
stats(MYKEY[0])
|
|
break;
|
|
case ">":
|
|
execute(MYKEY[0], command.slice(1).join(" "), USERPWD, conns);
|
|
break;
|
|
case ">restart":
|
|
restart(MYKEY[0])
|
|
break;
|
|
case ">stop":
|
|
stop(MYKEY[0])
|
|
break;
|
|
case ">start":
|
|
start(MYKEY[0])
|
|
break;
|
|
case ">login":
|
|
login(command[1], conns, MYKEY, USERNAME)
|
|
break;
|
|
case ">exit":
|
|
console.log("Sending close message...")
|
|
for (let conn of conns) {
|
|
conn.write(`CLOSED: ${publicKey.toString('hex')}`)
|
|
}
|
|
|
|
await sleep(2000)
|
|
process.exit()
|
|
break
|
|
default:
|
|
console.log("Command not found.")
|
|
}
|
|
} else {
|
|
for (const conn of conns)
|
|
|
|
conn.write(`${USERNAME[0]}: ${input}`)
|
|
|
|
}
|
|
console.log(`${USERNAME[0]}: ${input}`)
|
|
}
|
|
|
|
swarm.on('connection', conn => {
|
|
|
|
process.on('SIGINT', async () => {
|
|
console.log("Sending close message...")
|
|
for (let conn of conns) {
|
|
conn.write(`CLOSED: ${publicKey.toString('hex')}`)
|
|
}
|
|
|
|
await sleep(2000)
|
|
process.exit()
|
|
|
|
})
|
|
|
|
const name = b4a.toString(conn.remotePublicKey, 'hex')
|
|
console.log(`* got a connection from ${name} (${USERNAME[0]}) *`)
|
|
addUser(USERNAME[0], name)
|
|
sidebarBox.setLabel("Connected Peers: " + connectedUsers.length)
|
|
screen.render()
|
|
conns.push(conn)
|
|
conn.once('close', () => conns.splice(conns.indexOf(conn), 1))
|
|
conn.on('data', data => {
|
|
if (data.toString().startsWith('CLOSED:')) {
|
|
// Extract the key from the message string
|
|
const key = data.toString().split(':')[1].trim();
|
|
removeUser(key)
|
|
console.log(`Removing peer ${key}`);
|
|
(async () => {
|
|
|
|
await sleep(5000)
|
|
conns = conns.filter(c => c !== conn);
|
|
conn.destroy();
|
|
})();
|
|
|
|
} else {
|
|
console.log(`${data}`)
|
|
}
|
|
// Use the USERNAME if it has been set, otherwise use the public key
|
|
})
|
|
})
|
|
|
|
swarm.on('error', (err) => {
|
|
console.log('Error connecting to peer:', err);
|
|
});
|
|
|
|
|
|
// Join a common topic
|
|
const topic = process.argv[2] ? b4a.from(process.argv[2], 'hex') : crypto.randomBytes(32)
|
|
|
|
|
|
setTimeout(() => {
|
|
const discovery = swarm.join(topic, {
|
|
lookup: true,
|
|
announce: true,
|
|
timeout: 300000
|
|
|
|
});
|
|
|
|
// The flushed promise will resolve when the topic has been fully announced to the DHT
|
|
discovery.flushed().then(() => {
|
|
mainBox.setLabel("Topic: " + b4a.toString(topic, 'hex') + " (Share to connect)")
|
|
stdinBox.setLabel("To login: >login [TOKEN]")
|
|
screen.render()
|
|
})
|
|
}, 1000);
|
|
|
|
|
|
|
|
|
|
// Append the boxes to the screen
|
|
screen.append(mainBox);
|
|
screen.append(stdinBox);
|
|
screen.append(sidebarBox);
|
|
|
|
|
|
// Handle input in the stdinBox
|
|
stdinBox.on('submit', (input) => {
|
|
// handle the input here
|
|
// for example :
|
|
handleCommand(input);
|
|
// clear the input field
|
|
stdinBox.focus();
|
|
|
|
// screen.render();
|
|
});
|
|
|
|
// setInterval(() => {
|
|
// mainBox.scrollTo(mainBox.getScrollHeight());
|
|
// }, 1000);
|
|
stdinBox.focus();
|
|
// Render the screen
|
|
|
|
screen.render() |