You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
244 lines
7.6 KiB
244 lines
7.6 KiB
// This file will be depracated in favor of ./chat.js |
|
// AO shadowchat feature menu including bootstrap network server list browser, chatroom list on each server, and chatroom interface |
|
// Called shadowchat because no record is kept of the chat messages, and all connections happen E2E over tor |
|
// As this feature gets build, sensible standards must be developed around when tor addresses change hands, when users authenticate, etc |
|
import { aoEnv } from '../ao-lib/settings.js' |
|
import { isLoggedIn } from './session.js' |
|
import { startPublicBootstrap } from './bootstrap.js' |
|
import { headerStyle } from './styles.js' |
|
import { askQuestionText, promptMenu } from './welcome.js' |
|
import { AO_DEFAULT_HOSTNAME, startSocketListeners, socketStatus, socket, postEvent } from '../ao-lib/api.js' |
|
import { spawnSync } from 'child_process' |
|
|
|
import { fileURLToPath } from 'url' |
|
import path from 'path' |
|
const __filename = fileURLToPath(import.meta.url) |
|
const __dirname = path.dirname(__filename) |
|
|
|
const sleep = (ms = 550) => { return new Promise((r) => setTimeout(r, ms)) } |
|
|
|
// Prints a menu that allows you to join the global AO chatrooms |
|
export default async function chatMenu() { |
|
let answers = {} |
|
const PUBLIC_BOOTSTRAP_ENABLED = aoEnv('PUBLIC_BOOTSTRAP_ENABLED') |
|
if(PUBLIC_BOOTSTRAP_ENABLED) { |
|
// They previously enabled public bootstrapping, so check to make sure it is working and then hide the option |
|
// todo: start and then verify functioning of p2p boostrap method here |
|
// if it's already started don't start it again |
|
//if(!publicBootstrapStarted) { |
|
console.log("\nBootstrapping public AO swarm...") |
|
startPublicBootstrap() |
|
console.log("Bootstrapped (just kidding)") |
|
//} |
|
//answers['chat_menu'] = 'Enable p2p bootstrap' |
|
} |
|
const chatChoices = [ |
|
{ title: 'Join public chatroom', value: 'browse_chatrooms', short: 'public chatrooms' }, |
|
{ title: 'Join chatroom', value: 'join_chat', short: 'join chat' }, |
|
'Address Book', |
|
'Back to AO Menu', |
|
] |
|
const answer = await promptMenu(chatChoices, 'AO Public Chatrooms') |
|
switch(answer) { |
|
case 'browse_chatrooms': |
|
const loggedIn = isLoggedIn() |
|
if(!isLoggedIn) { |
|
console.log('Please start AO server and log in first.') |
|
return true |
|
} |
|
console.log('Logged in as:', aoEnv('AO_CLI_SESSION_USERNAME')) |
|
|
|
const onAuthenticated = () => { |
|
//console.log('Websocket connected and authenticated.') |
|
} |
|
const onEvent = (event) => { |
|
console.log('\nEvent received on websocket:', event) // todo: still receiving excess task-resets every 5 minutes |
|
} |
|
if(socketStatus !== 'authenticationSuccess') { |
|
await startSocketListeners(onAuthenticated, onEvent) |
|
await sleep(400) |
|
} |
|
console.log('socketStatus is', socketStatus) |
|
if(socketStatus !== 'authenticationSuccess') { |
|
console.log('Failed to connect to websocket. Make sure your AO server is running, and if you have a custom config set your target server in ao-cli.') |
|
return true |
|
} |
|
console.log('Websocket successfully connected to listen for events.') |
|
while(await browseChatrooms()) {} |
|
break |
|
case 'join_chat': |
|
console.log('Launching Simplex Chat...') |
|
const doNothing = () => {} |
|
process.on('SIGINT', doNothing); |
|
spawnSync('simplex-chat', [], { stdio: 'inherit' }) |
|
process.removeListener('SIGINT', doNothing) |
|
break |
|
case 'Address Book': |
|
console.log('The point of this address book is to make it possible to type short, one-word names and have them resolve to tor addresses.') |
|
console.log('Name a piece of data by saying name=data. For example, doge=5uc41o1...onion. Then \'doge\' will return the .onion address.') |
|
console.log('Querying with any synonym in a chain will return the final meanings they all point to.') |
|
console.log('Keys can have multiple values.') |
|
break |
|
default: |
|
return false |
|
} |
|
return true |
|
} |
|
|
|
async function getLocalRooms() { |
|
return new Promise((resolve, reject) => { |
|
socket.on('rooms_list', event => { |
|
socket.off('rooms_list') |
|
resolve(event) |
|
}) |
|
socket.emit('get_rooms') |
|
}) |
|
} |
|
|
|
async function browseChatrooms() { |
|
if(socketStatus !== 'authenticationSuccess') { |
|
console.log('Websocket is not connected, going back.') |
|
return false |
|
} |
|
console.log('Getting list of chatrooms...') |
|
const chatrooms = await getLocalRooms() |
|
// Get list of all servers and display |
|
// todo: check if websocket can work without authentication |
|
|
|
// For each server, request its list of channels |
|
console.log('Chatrooms on this server:', chatrooms) |
|
|
|
//await chatInRoom('HUB') |
|
return await chatroomScreen('HUB') |
|
|
|
// Display the list as a menu with a heading for each server |
|
} |
|
|
|
async function chatInRoom(room) { |
|
console.log('Joining room...') |
|
socket.emit('join_room', room) |
|
return new Promise(async (resolve, reject) => { |
|
const leaveChatroom = () => { |
|
console.log('Leaving room...') |
|
socket.emit('leave_room', room) |
|
socket.off('chat') |
|
resolve() |
|
} |
|
|
|
socket.on('chat', event => { |
|
const chatLine = event.name ? event.name + ': ' + event.message : 'Anon:' + event.message |
|
console.log(/*new Date().toLocaleTimeString(), */'\n' + chatLine) |
|
}) |
|
|
|
const username = aoEnv('AO_CLI_SESSION_USERNAME') |
|
let message |
|
do { |
|
message = await askQuestionText('> ') |
|
switch(message.toLowerCase()) { |
|
case 'exit': |
|
case 'leave': |
|
case 'back': |
|
case 'quit': |
|
case false: |
|
resolve(false) |
|
default: |
|
//socket.emit('chat', room, message, username || undefined) |
|
postEvent({ |
|
type: 'shadowchat', |
|
room: room, |
|
name: username || undefined, |
|
message: message, |
|
}) |
|
} |
|
} while(message !== 'exit') |
|
}) |
|
} |
|
|
|
/*async function chatroomScreen(room) { |
|
var ui = new inquirer.ui.BottomBar() |
|
ui.render() |
|
console.log(ui) |
|
// pipe a Stream to the log zone |
|
//outputStream.pipe(ui.log) |
|
|
|
// Or simply write output |
|
ui.log.write('something just happened.'); |
|
ui.log.write('Almost over, standby!'); |
|
|
|
// During processing, update the bottom bar content to display a loader |
|
// or output a progress bar, etc |
|
ui.updateBottomBar('new bottom bar content'); |
|
}*/ |
|
|
|
import blessed from 'blessed' |
|
export async function chatroomScreen(room) { |
|
const screen = blessed.screen({ |
|
smartCSR: true, |
|
ignoreLocked: ['C-c'], |
|
dockBorders: true, |
|
title: room |
|
}) |
|
|
|
const chatlog = blessed.log({ |
|
top: 0, |
|
left: 0, |
|
bottom: 2, |
|
width: '100%', |
|
content: '{bold}' + room + '{/bold}', |
|
tags: true, |
|
scrollable: true, |
|
border: { |
|
type: 'line' |
|
}, |
|
style: { |
|
fg: 'white', |
|
bg: 'black', |
|
border: { |
|
fg: '#f0f0f0' |
|
}, |
|
} |
|
}) |
|
screen.append(chatlog); |
|
|
|
const input = blessed.textbox({ |
|
bottom: 0, |
|
left: 0, |
|
right: 0, |
|
height: 3, |
|
content: 'Type message here', |
|
inputOnFocus: true, |
|
border: { |
|
type: 'line' |
|
}, |
|
style: { |
|
fg: 'white', |
|
bg: 'blue', |
|
border: { |
|
fg: '#f0f0f0' |
|
}, |
|
} |
|
}) |
|
input.on('submit', (event) => { |
|
chatlog.log('new submission:', event) |
|
input.clearValue() |
|
input.focus() |
|
}) |
|
input.on('keypress', (event) => { |
|
chatlog.add('got keypress:', event) |
|
}) |
|
screen.append(input) |
|
//screen.focusPop() |
|
chatlog.add('screen.focus:', screen.focus) |
|
input.focus() |
|
|
|
screen.render() |
|
|
|
return new Promise(async (resolve, reject) => { |
|
//screen.key(['C-c'], () => process.exit(0)) |
|
screen.key(['escape', 'C-c'], function(ch, key) { |
|
chatlog.log('Exiting chatroom...') |
|
screen.destroy() |
|
resolve(false) |
|
}) |
|
}) |
|
}
|
|
|