Total change, Adding a GUI

This commit is contained in:
raven 2023-01-12 13:35:30 -05:00
parent 4d31a133a9
commit 202a920815
2 changed files with 226 additions and 108 deletions

View File

@ -1,4 +1,4 @@
{ x{
"name": "sshchat", "name": "sshchat",
"version": "1.0.0", "version": "1.0.0",
"description": "A peer to peer chat client that uses hyper-protocol and connects to the ssh.surf API", "description": "A peer to peer chat client that uses hyper-protocol and connects to the ssh.surf API",
@ -14,9 +14,11 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"b4a": "^1.6.1", "b4a": "^1.6.1",
"blessed": "^0.1.81",
"graceful-goodbye": "^1.2.0", "graceful-goodbye": "^1.2.0",
"hypercore-crypto": "^3.3.0", "hypercore-crypto": "^3.3.0",
"hyperswarm": "^4.3.5", "hyperswarm": "^4.3.5",
"neo-blessed": "^0.2.0",
"readline": "^1.3.0", "readline": "^1.3.0",
"unirest": "^0.6.0" "unirest": "^0.6.0"
} }

View File

@ -1,3 +1,4 @@
const blessed = require('neo-blessed');
// Require the needed libs // Require the needed libs
const Hyperswarm = require('hyperswarm') const Hyperswarm = require('hyperswarm')
const crypto = require('hypercore-crypto') const crypto = require('hypercore-crypto')
@ -22,18 +23,113 @@ let DAPI_KEY
let LOGGEDIN = false let LOGGEDIN = false
let MYKEY = [] let MYKEY = []
let conns = [] let conns = []
let connectedUsers = [];
let USERNAME = ["annon" + rand] let USERNAME = ["annon" + rand]
function sleep(ms) { function sleep(ms) {
return new Promise((resolve) => { return new Promise((resolve) => {
setTimeout(resolve, ms); setTimeout(resolve, ms);
}); });
} }
async function clearCursor() {
readline.moveCursor(process.stdout, 0, -2) // up one line function addUser(user, peerId) {
readline.clearLine(process.stdout, 0) // from cursor to end connectedUsers.push({ name: user, peerId: peerId });
readline.moveCursor(process.stdout, 0, 2) // up one line 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 // Generate a random public key
@ -48,133 +144,153 @@ const commandFiles = fs.readdirSync(commandDir);
const commands = {} const commands = {}
for (const file of commandFiles) { for (const file of commandFiles) {
const commandName = file.split(".")[0] const commandName = file.split(".")[0]
require(`${commandDir}/${file}`) require(`${commandDir}/${file}`)
const command = require(`${commandDir}/${file}`); const command = require(`${commandDir}/${file}`);
commands[commandName] = command; commands[commandName] = command;
} }
async function handleCommand(input) { async function handleCommand(input) {
if (input.startsWith("!") || input.startsWith(">")) { if (input.startsWith("!") || input.startsWith(">")) {
const command = input.split(" ") const command = input.split(" ")
if (!command) return consoile.log("Please either send a message or enter a command.") if (!command) return consoile.log("Please either send a message or enter a command.")
switch (command[0]) { switch (command[0]) {
case "!": case "!":
AIRequest(command.slice(1).join(" ")) AIRequest(command.slice(1).join(" "))
break; break;
case ">cd": case ">cd":
USERPWD = await changeDir(command[1], USERPWD); USERPWD = await changeDir(command[1], USERPWD);
console.log(USERPWD) console.log(USERPWD)
break; break;
case ">stats": case ">stats":
stats(MYKEY[0]) stats(MYKEY[0])
break; break;
case ">": case ">":
execute(MYKEY[0], command.slice(1).join(" "), USERPWD, conns); execute(MYKEY[0], command.slice(1).join(" "), USERPWD, conns);
break; break;
case ">restart": case ">restart":
restart(MYKEY[0]) restart(MYKEY[0])
break; break;
case ">stop": case ">stop":
stop(MYKEY[0]) stop(MYKEY[0])
break; break;
case ">start": case ">start":
start(MYKEY[0]) start(MYKEY[0])
break; break;
case ">login": case ">login":
login(command[1], conns, MYKEY, USERNAME) login(command[1], conns, MYKEY, USERNAME)
break; break;
case ">exit": case ">exit":
console.log("Sending close message...") console.log("Sending close message...")
for (let conn of conns) { for (let conn of conns) {
conn.write(`CLOSED: ${publicKey.toString('hex')}`) conn.write(`CLOSED: ${publicKey.toString('hex')}`)
}
await sleep(2000)
process.exit()
break
default:
console.log("Command not found.")
} }
} else {
await sleep(2000) for (const conn of conns)
process.exit()
break conn.write(`${USERNAME[0]}: ${input}`)
default:
console.log("Command not found.")
} }
} else { console.log(`${USERNAME[0]}: ${input}`)
for (const conn of conns)
conn.write(`${USERNAME[0]}: ${input}`)
}
console.log(`${USERNAME[0]}: ${input}`)
clearCursor()
} }
swarm.on('connection', conn => { swarm.on('connection', conn => {
process.on('SIGINT', async () => { process.on('SIGINT', async () => {
console.log("Sending close message...") console.log("Sending close message...")
for (let conn of conns) { for (let conn of conns) {
conn.write(`CLOSED: ${publicKey.toString('hex')}`) conn.write(`CLOSED: ${publicKey.toString('hex')}`)
} }
await sleep(2000) await sleep(2000)
process.exit() process.exit()
}) })
const name = b4a.toString(conn.remotePublicKey, 'hex') const name = b4a.toString(conn.remotePublicKey, 'hex')
console.log(`* got a connection from ${name} (${USERNAME[0]}) *`) console.log(`* got a connection from ${name} (${USERNAME[0]}) *`)
conns.push(conn) addUser(USERNAME[0], name)
conn.once('close', () => conns.splice(conns.indexOf(conn), 1)) sidebarBox.setLabel("Connected Peers: " + connectedUsers.length)
conn.on('data', data => { screen.render()
if (data.toString().startsWith('CLOSED:')) { conns.push(conn)
// Extract the key from the message string conn.once('close', () => conns.splice(conns.indexOf(conn), 1))
const key = data.toString().split(':')[1].trim(); conn.on('data', data => {
console.log(`Removing peer ${key}`); if (data.toString().startsWith('CLOSED:')) {
(async () => { // 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) await sleep(5000)
conns = conns.filter(c => c !== conn); conns = conns.filter(c => c !== conn);
conn.destroy(); conn.destroy();
})(); })();
} else { } else {
console.log(`${data}`) console.log(`${data}`)
} }
// Use the USERNAME if it has been set, otherwise use the public key // Use the USERNAME if it has been set, otherwise use the public key
}) })
}) })
swarm.on('error', (err) => { swarm.on('error', (err) => {
console.log('Error connecting to peer:', err); console.log('Error connecting to peer:', err);
}); });
// Use readline to accept input from the user
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
// When the user inputs a line of text, broadcast it to all connections
rl.on('line', input => {
handleCommand(input)
})
// Join a common topic // Join a common topic
const topic = process.argv[2] ? b4a.from(process.argv[2], 'hex') : crypto.randomBytes(32) const topic = process.argv[2] ? b4a.from(process.argv[2], 'hex') : crypto.randomBytes(32)
setTimeout(() => { setTimeout(() => {
const discovery = swarm.join(topic, { const discovery = swarm.join(topic, {
lookup: true, lookup: true,
announce: true, announce: true,
timeout: 300000 timeout: 300000
}); });
// The flushed promise will resolve when the topic has been fully announced to the DHT // The flushed promise will resolve when the topic has been fully announced to the DHT
discovery.flushed().then(() => { discovery.flushed().then(() => {
console.log(`joined topic: ${b4a.toString(topic, 'hex')}\n(Share this key to others so they may join.)`) mainBox.setLabel("Topic: " + b4a.toString(topic, 'hex') + " (Share to connect)")
console.log('You are now in a chatroom for your topic, feel free to chat.\n') stdinBox.setLabel("To login: >login [TOKEN]")
console.log('Want to login to the SSH.SURF API? Type ">login [APIKEY]" to login.\nPease close using CTRL+X or use the >exit command\n') screen.render()
}) })
}, 1000); }, 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()