|
|
|
// 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)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|