UNPKG

@ztiknl/sara

Version:

Sentient Artificial Responsive Agent

451 lines (398 loc) 14.2 kB
// include fs module const fs = require('fs'); // include colors module const chalk = require('chalk'); // include colored responses module const response = require('./response.js'); // include help function module const help = require('./help.js'); // include sfx module const sfx = require('./sfx.js'); // include voice synthesis module const voice = require('./voice.js'); // include speech recognition module const hearing = require('./hearing.js'); // include vision module const vision = require('./vision.js'); // include plugins module const plugins = require('./plugins.js'); // include bootstrap module const bootstrap = require('./bootstrap.js'); // set start vars var commands = []; const readline = require('readline'), util = require('util'), EventEmitter = require('events'); readline.cursorTo(process.stdout, 0, process.stdout.rows); class MyEmitter extends EventEmitter {} const myEmitter = new MyEmitter(); var rl; var completions = []; if (response.getcolors()) { var myPrompt = chalk.bold.magentaBright('<input> '); } else { var myPrompt = '<input> '; } module.exports = { question: question, write: write, start: start, getPrompt: function() { return myPrompt; }, setCompletion: function(obj) { completions = (typeof obj == 'object') ? obj : completions; }, setPrompt: function(str) { myPrompt = str; rl.setPrompt(myPrompt); }, on: function(line) { myEmitter.on.apply(myEmitter, arguments); } } function question(string) { return new Promise(resolve => { string = '<'+string+'> ' if (response.getcolors()) { string = chalk.bold.magentaBright(string); } rl.question(string, (answer) => { resolve(answer) }) }) } function runcmd(string) { return new Promise(resolve => { let result = plugins.match(string).then((result) => { resolve(result); }) }) } function write(string) { rl.write(string); } function start(strPrompt, callback) { if (response.getcolors()) { myPrompt = chalk.bold.magentaBright('<input> '); } else { myPrompt = '<input> '; } rl = readline.createInterface({ input: process.stdin, output: process.stdout, completer: completer }); rl.setPrompt(myPrompt); rl.on('line', function(line) { rl.history.push(line); // myEmitter.emit('line', line); line = line.trim(); var regex = /^(?:sara|sarah|s.a.r.a.|s.a.r.a)?\s?,?\s?(?:please)?\s?(?:can\syou|could\syou)?\s?(?:tell\sme|let\sme\sknow)?\s?(?:please)?\s?/i line = line.replace(regex, '').trim(); var regex = /\.?,?\s?(?:sara|sarah|s.a.r.a.|s.a.r.a)?\s?(?:please)?\s?(?:\?|\!)?\s?$/i line = line.replace(regex, '').trim(); line = line.replace(/(?:\s)+/g, ' ').trim(); var finalcmd = false; splitcommands(line); function splitcommands(cmd) { var end = cmd.indexOf(")"); if (end != -1) { var sub = cmd.substr(0, end); var start = sub.lastIndexOf("("); if (start != -1) { sub = sub.substr(start+1); var subcommands = true; finalcmd = true; } else { var subcommands = false; } } else { var subcommands = false; } if (!subcommands) { if (finalcmd) { response.conlog('prompt', 'processing final command', 'info'); } else { response.conlog('prompt', 'processing command', 'info'); } processcmd(cmd).then((result) => { if (Array.isArray(result)) { response.conlog('prompt', result[0], 'response'); if (result[2] != undefined) { voice.synthesize(result[1], result[2]); } else { voice.synthesize(result[1]); } } else { response.conlog('prompt', result, 'response'); voice.synthesize(result); } }); } else { response.conlog('prompt', 'processing subcommand', 'info'); subcommands = true; processcmd(sub).then((result) => { if (Array.isArray(result)) { var rslt = result[0]; } else { var rslt = result; } response.conlog('prompt', rslt, 'data'); stline = cmd.substr(0, start); enline = cmd.substr(end+1); splitcommands(stline+rslt+enline); }); } } function processcmd(cmd) { return new Promise(resolve => { if (cmd == '' || cmd == ' ') { response.conlog('input', 'no command entered', 'error'); } else { response.conlog('input', cmd, 'command'); if (cmd.toLowerCase() == 'help' || cmd.substr(0, 4).toLowerCase() == 'help') { help.get(cmd.toLowerCase()); } else if (cmd.substr(0, 9).toLowerCase() == 'edit help') { if (cmd.toLowerCase() == 'edit help') { response.conlog('help', 'Please enter the topic to edit', 'error'); } else { cmd = cmd.substr(10, cmd.length).trim().toLowerCase(); let path = './documentation/'+cmd+'.json'; fs.readFile(path, 'utf8', function (err, data) { if (err) { response.conlog('help', 'Couldn\'t read JSON file: '+err.message, 'error'); } var obj = JSON.parse(data); help.questions(obj.name, obj.description, obj.explanation, 'update') }); } } else if (cmd.toLowerCase() == 'add help') { help.questions(null, null, null, null, 'add'); } else if (cmd.toLowerCase() == 'list help' || cmd.toLowerCase() == 'help list') { help.list(); } else if (cmd.toLowerCase() == 'start hearing' || cmd.toLowerCase() == 'start listening') { hearing.listen().then((result) => { resolve(result); });; } else if (cmd.toLowerCase() == 'stop hearing' || cmd.toLowerCase() == 'stop listening') { hearing.stop().then((result) => { resolve(result); });; } else if (cmd.toLowerCase() == 'start voice' || cmd.toLowerCase() == 'start talking' || cmd.toLowerCase() == 'start speaking') { voice.start().then((result) => { resolve(result); }); } else if (cmd.toLowerCase() == 'stop voice' || cmd.toLowerCase() == 'stop talking' || cmd.toLowerCase() == 'stop speaking') { voice.stop().then((result) => { resolve(result); }); } else if (cmd.toLowerCase() == 'list voice' || cmd.toLowerCase() == 'voice list' || cmd.toLowerCase() == 'list voices' || cmd.toLowerCase() == 'voices list') { voice.list().then((result) => { resolve(result); }); } else if (cmd.toLowerCase() == 'stop command execution' || cmd.toLowerCase() == 'start command parsing') { hearing.cmdtoprompt().then((result) => { resolve(result); }); } else if (cmd.toLowerCase() == 'start command execution' || cmd.toLowerCase() == 'stop command parsing') { hearing.cmdexecute().then((result) => { resolve(result); }); } else if (cmd.toLowerCase() == 'start vision' || cmd.toLowerCase() == 'start watching') { vision.start(); } else if (cmd.toLowerCase() == 'stop vision' || cmd.toLowerCase() == 'stop watching') { vision.stop(); } else if (cmd.toLowerCase() == 'silence' || cmd.toLowerCase() == 'quiet') { voice.silence().then((result) => { resolve(result); }); } else if (cmd.toLowerCase() == 'start sound effects' || cmd.toLowerCase() == 'start sfx') { sfx.start().then((result) => { resolve(result); }); } else if (cmd.toLowerCase() == 'stop sound effects' || cmd.toLowerCase() == 'stop sfx') { sfx.stop().then((result) => { resolve(result); }); } else if (cmd.toLowerCase() == 'start colors') { if (!response.getcolors()) { rl.setPrompt(chalk.bold.magentaBright('<input> ')); } response.setcolors() } else if (cmd.toLowerCase() == 'stop colors') { if (response.getcolors()) { rl.setPrompt('<input> '); } response.unsetcolors(); } else if (cmd.toLowerCase() == 'start bootstrap') { bootstrap.start(); } else if (cmd.toLowerCase() == 'stop bootstrap') { bootstrap.stop(); } else if (cmd.toLowerCase() == 'list bootstrap' || cmd.toLowerCase() == 'bootstrap list') { bootstrap.list().then((result) => { resolve(result); }); } else if (cmd.toLowerCase() == 'start verbose') { response.setverbose(); } else if (cmd.toLowerCase() == 'stop verbose') { response.unsetverbose(); } else if (cmd.toLowerCase() == 'exit') { rl.close(); } else { runcmd(cmd).then((result) => { resolve(result); }); } } }) } }); rl.on('close', function() { sfx.output('shutdown'); response.conlog('sara', 'shutting down', 'info'); myEmitter.emit('close'); return process.exit(1); }); rl.on('SIGINT', function() { rl.clearLine(); sfx.output('shutdown'); response.conlog('sara', 'shutting down', 'info'); if (!myEmitter.emit('SIGINT', rl)) process.exit(1); }); rl.prompt(); hiddenOverwrite(); consoleOverwrite(); response.conlog('prompt', 'input prompt activated', 'status'); } function hiddenOverwrite() { rl._refreshLine = (function(refresh) { //https://github.com/nodejs/node/blob/v9.5.0/lib/readline.js#L335 return function _refreshLine() { refresh.apply(rl); } })(rl._refreshLine); //https://github.com/nodejs/node/blob/v9.5.0/lib/readline.js#L442 function _insertString(c) { if (this.cursor < this.line.length) { var beg = this.line.slice(0, this.cursor); var end = this.line.slice(this.cursor, this.line.length); this.line = beg + c + end; this.cursor += c.length; this._refreshLine(); } else { this.line += c; this.cursor += c.length; if (this._getCursorPos().cols === 0) { this._refreshLine(); } else { this._writeToOutput(c); } // a hack to get the line refreshed if it's needed this._moveCursor(0); } }; rl._insertString = _insertString; } function consoleOverwrite() { var myWrite = function(stream, string, errorhandler) { process.stdout.write(rl.columns); var nbline = Math.ceil((rl.line.length + 3) / rl.columns); var text = ''; text += '\n\r\x1B[' + nbline + 'A\x1B[0J'; text += string + '\r'; text += Array(nbline).join('\r\x1B[1E'); stream.write(text, errorhandler); rl._refreshLine(); }; const kGroupIndent = Symbol('groupIndent'); console[kGroupIndent] = ''; let MAX_STACK_MESSAGE; function noop() {} //Internal function console.js : https://github.com/nodejs/node/blob/v9.5.0/lib/console.js#L96 function write(ignoreErrors, stream, string, errorhandler, groupIndent) { if (groupIndent.length !== 0) { if (string.indexOf('\n') !== -1) { string = string.replace(/\n/g, `\n${groupIndent}`); } string = groupIndent + string; } string += '\n'; if (ignoreErrors === false) return myWrite(stream, string, errorhandler); try { stream.once('error', noop); myWrite(stream, string, errorhandler); } catch (e) { if (MAX_STACK_MESSAGE === undefined) { try { function a() { a(); } } catch (err) { MAX_STACK_MESSAGE = err.message; } } if (e.message === MAX_STACK_MESSAGE && e.name === 'RangeError') throw e; } finally { stream.removeListener('error', noop); } } console.log = function log(...args) { write(console._ignoreErrors, console._stdout, util.format.apply(null, args), console._stdoutErrorHandler, console[kGroupIndent]); }; console.debug = console.log; console.info = console.log; console.dirxml = console.log; console.warn = function warn(...args) { write(console._ignoreErrors, console._stderr, util.format.apply(null, args), console._stderrErrorHandler, console[kGroupIndent]); }; console.error = console.warn; console.dir = function dir(object, options) { options = Object.assign({ customInspect: false }, options); write(console._ignoreErrors, console._stdout, util.inspect(object, options), console._stdoutErrorHandler, console[kGroupIndent]); }; } function completer(line) { var n = line.trim().split(' '); line = n[n.length - 1]; var hits = completions.filter(function(c) { return c.indexOf(line) == 0; }); if (hits.length == 1) { return [hits, line]; } else { var list = '', l = 0, c = '', t = hits.length ? hits : completions; for (var i = 0; i < t.length; i++) { c = t[i].replace(/(\s*)$/g, '') if (list != '') { list += ', '; } if (((list + c).length + 4 - l) > process.stdout.columns) { list += '\n'; l = list.length; } list += c; } response.conlog('autocomplete', list, 'options'); return [hits, line]; } }