commit dd996aecaf10a3a2ef94f02f99a02adb497a8573 Author: raven Date: Sun Jan 8 22:03:13 2023 -0500 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/README.md b/README.md new file mode 100644 index 0000000..e00bdde --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +# SSH Chat + +A Peer to Peer chat client using Hyper-Protocol. + +To install: + +`git clone git@git.codingvm.codes:snxraven/sshChat-CLI.git` + +`cd sshChat-CLI` + +`npm i` + +To run a new topic (room): + +`node sshchat.mjs` + +This will give you a connection topic to share: + +``` +/sshChat ❯ ✦ node sshChat.mjs +joined topic: 6076c0903ad293e24c10fceb501fe7b02425f6d26c7a5b2d015abd07e3e6b17b +(Share this key to others so they may join.) +You are now in a chatroom for your topic, feel free to chat. +``` + + +To connect to an altready made topic (room) pass the hash on start up: + + +`node sshchat.mjs 6076c0903ad293e24c10fceb501fe7b02425f6d26c7a5b2d015abd07e3e6b17b` + +# Commands + +/login [API KEY] - Login to the API + +> command here - Send a command to your container + +/start - Start your container + +/stop - Stop your container + +/restart - restart your container + +/stats - Get the containers stats + +If a user does not login they will have a random annon1234 username. diff --git a/package.json b/package.json new file mode 100644 index 0000000..562eb06 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "sshchat", + "version": "1.0.0", + "description": "A peer to peer chat client that uses hyper-protocol and connects to the ssh.surf API", + "main": "sshChat.mjs", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git@git.codingvm.codes:snxraven/sshChat-CLI.git" + }, + "author": "Raven Scott", + "license": "ISC", + "dependencies": { + "b4a": "^1.6.1", + "graceful-goodbye": "^1.2.0", + "hypercore-crypto": "^3.3.0", + "hyperswarm": "^4.3.5", + "readline": "^1.3.0", + "unirest": "^0.6.0" + } +} diff --git a/sshChat.mjs b/sshChat.mjs new file mode 100644 index 0000000..1f8bc6c --- /dev/null +++ b/sshChat.mjs @@ -0,0 +1,200 @@ + +import Hyperswarm from 'hyperswarm' +import goodbye from 'graceful-goodbye' +import crypto from 'hypercore-crypto' +import b4a from 'b4a' +import readline from 'readline' +import unirest from "unirest"; +let rand = Math.floor(Math.random() * 99999).toString(); + +// Generate a random public key +const publicKey = crypto.randomBytes(32) + +// Create the swarm and pass in the public key +const swarm = new Hyperswarm() +goodbye(() => swarm.destroy()) + +// Keep track of all connections and USERNAMEs +const conns = [] +const names = {} +let DAPI_KEY = {} +let USERNAME = "annon" + rand +let LOGGEDIN = false + +async function clearCursor() { + readline.moveCursor(process.stdout, 0, -2) // up one line + readline.clearLine(process.stdout, 0) // from cursor to end + readline.moveCursor(process.stdout, 0, 2) // up one line +} + +// API Functions +async function getUSERNAME(key) { + let requestUSERNAME = await unirest + .get('https://api.discord-linux.com/hello') + .headers({ 'Accept': 'application/json', 'Content-Type': 'application/json', 'x-discord-linux-auth': key }) + return requestUSERNAME.body.message.replace("Hello, ", "").replace("!", "") +} + +async function runCMD(key, cmd, pwd){ + let requestData = await unirest + .post('https://api.discord-linux.com/exec') + .headers({'Accept': 'application/json', 'Content-Type': 'application/json', + 'x-discord-linux-auth': key}) + .send({"cmd": cmd, "pwd": pwd}) + return requestData.body.stdout +} + +async function startContainer(key){ + let startContainer = await unirest + .get('https://api.discord-linux.com/start') + .headers({'Accept': 'application/json', 'Content-Type': 'application/json', + 'x-discord-linux-auth': key}) + return startContainer.body.completed +} + + +async function stopContainer(key){ + let stopContainer = await unirest + .get('https://api.discord-linux.com/stop') + .headers({'Accept': 'application/json', 'Content-Type': 'application/json', + 'x-discord-linux-auth': key}) + return stopContainer.body.completed +} + +async function restartContainer(key){ + let restartContainer = await unirest + .get('https://api.discord-linux.com/restart') + .headers({'Accept': 'application/json', 'Content-Type': 'application/json', + 'x-discord-linux-auth': key}) + return restartContainer.body.completed +} + +async function getStats(key){ + let getStats = await unirest + .get('https://api.discord-linux.com/restart') + .headers({'Accept': 'application/json', 'Content-Type': 'application/json', + 'x-discord-linux-auth': key}) + console.log(getStats.body) + return getStats.body.data +} + + +swarm.on('connection', conn => { + const name = b4a.toString(conn.remotePublicKey, 'hex') + console.log(`* got a connection from ${name} (${USERNAME}) *`) + conns.push(conn) + conn.once('close', () => conns.splice(conns.indexOf(conn), 1)) + conn.on('data', data => { + // Use the USERNAME if it has been set, otherwise use the public key + console.log(`${data}`) + }) +}) + +// 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 => { + + + const signIn = input.match(/^\/login (\S+)$/) + if (signIn) { + const APIKEY = signIn[1] + DAPI_KEY = { "key": APIKEY } + getUSERNAME(APIKEY).then((data) => { + USERNAME = data + // const name = b4a.toString(publicKey, 'hex') + // names[name] = USERNAME + for (const conn of conns) { + conn.write(`${USERNAME} has logged in!`) + } + clearCursor() + console.log(`Weclcome ${USERNAME} you are now logged in!`) + return LOGGEDIN = true + }); + } + + const execute = input.startsWith(">") + if (execute) { + let inputdata = input.split(2) + const cmdToRun = inputdata.join(" ").replace("> ", "").replace(">","") + runCMD(DAPI_KEY.key, cmdToRun, "/").then((data) => { + console.log(data) + for (const conn of conns) { + conn.write(`${USERNAME} ran ${cmdToRun}: \n` + data) + } + }) +} + + +const start = input.match(/^\/start (\S+)$/) +if (start) { + const cmdToRun = start[1] + startContainer(DAPI_KEY.key).then((data) => { + for (const conn of conns) { + console.log(data) + conn.write(`${USERNAME} ran start: \n` + data) + } +}) +} + + +const stop = input.match(/^\/stop (\S+)$/) +if (stop) { + const cmdToRun = stop[1] + stopContainer(DAPI_KEY.key).then((data) => { + for (const conn of conns) { + console.log(data) + conn.write(`${USERNAME} ran stop: \n` + data) + } +}) +} + +const restart = input.match(/^\/restart (\S+)$/) +if (restart) { + const restart = restart[1] + restartContainer(DAPI_KEY.key).then((data) => { + for (const conn of conns) { + console.log(data) + conn.write(`${USERNAME} ran restart: \n` + data) + } +}) +} + + +const stats = input.match(/^\/stats (\S+)$/) +if (stats) { + const stats = stats[1] + getStats(DAPI_KEY.key).then((data) => { + for (const conn of conns) { + console.log(data) + conn.write(`${USERNAME} ran stats: \n` + data) + } +}) +} + + else { + // if (!LOGGEDIN) return console.log("Please login using /login [API KEY]") + if (input.match(/^\/login (\S+)$/)) return + for (const conn of conns) { + conn.write(`${USERNAME}: ${input}`) + } + console.log(`${USERNAME}: ${input}`) + // Clear the input line\ + clearCursor() + } +}) + +// Join a common topic +const topic = process.argv[2] ? b4a.from(process.argv[2], 'hex') : crypto.randomBytes(32) +const discovery = swarm.join(topic, { client: true, server: true }) + +// The flushed promise will resolve when the topic has been fully announced to the DHT +discovery.flushed().then(() => { + console.log(`joined topic: ${b4a.toString(topic, 'hex')}\n(Share this key to others so they may join.)`) + console.log('You are now in a chatroom for your topic, feel free to chat.\n') + console.log('Want to login to the SSH.SURF API? Type "/login [APIKEY]" to login.') +}) \ No newline at end of file