├── .gitignore ├── actions ├── compile │ ├── index.js │ ├── javascript.js │ └── test │ │ ├── arms.js │ │ ├── cat.js │ │ ├── cat.min.js │ │ ├── eyes.js │ │ ├── hands.js │ │ ├── head.js │ │ └── legs.js ├── how.js ├── index.js ├── relabel.js ├── repeat.js ├── uglify.js ├── validate.js ├── watch.js ├── what.js └── who.js ├── bin ├── .DS_Store ├── classifier.json ├── lexicon.json └── nodebot ├── brain ├── analyze.js ├── formatter.js ├── interaction.js ├── language │ ├── .DS_Store │ └── index.js ├── lexicon.js └── time │ └── index.js ├── nodebot.js ├── package.json ├── readme.md ├── server ├── index.html ├── index.js ├── public │ ├── 404.html │ ├── config.rb │ ├── images │ │ ├── apple-touch-icon-114x114.png │ │ ├── apple-touch-icon-72x72.png │ │ ├── apple-touch-icon.png │ │ ├── brushed_alu.png │ │ ├── dark_dotted.png │ │ └── favicon.ico │ ├── javascripts │ │ ├── nodebot.js │ │ └── tabs.js │ ├── robots.txt │ ├── sass │ │ ├── partials │ │ │ ├── _base_type.scss │ │ │ ├── _buttons.scss │ │ │ ├── _custom.scss │ │ │ ├── _forms.scss │ │ │ ├── _grid.scss │ │ │ ├── _images.scss │ │ │ ├── _lists.scss │ │ │ ├── _misc.scss │ │ │ ├── _mixins.scss │ │ │ ├── _reset.scss │ │ │ ├── _tabs.scss │ │ │ └── _variables.scss │ │ └── style.scss │ └── stylesheets │ │ └── style.css └── routes.js ├── test ├── format-test.js ├── language-test.js └── lexicon-test.js └── validator ├── css.js ├── html.js ├── javascript.js ├── jshint_config.js ├── reporter.js └── website.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store -------------------------------------------------------------------------------- /actions/compile/index.js: -------------------------------------------------------------------------------- 1 | // Compile 2 | // -------------------------------------------------- // 3 | 4 | // Uglify reqs 5 | var fs = require('fs'); 6 | 7 | // Filters 8 | var lex = Nodebot.lexicon, 9 | fileEx = lex.file['regular expression'], 10 | urlEx = lex.url['regular expression'], 11 | jsEx = lex.javascript['regular expression'], 12 | 13 | supported = ['javascript']; 14 | 15 | 16 | // Compresses files 17 | // -------------------------------------------------- // 18 | 19 | function compressor (directory, filetype, filename) { 20 | 21 | var filter = Nodebot.lexicon[filetype]["regular expression"], 22 | newFile = filename || filetype + ".min" + lex[filetype].mime, 23 | compress = require("./" + filetype); 24 | 25 | fs.readdir(directory, function(err, files) { 26 | 27 | if (err) return false; 28 | 29 | files = files.map(function(f) { 30 | if (filename && f === filename) { 31 | } else { 32 | return directory + "/" + f; 33 | } 34 | }); 35 | 36 | try { 37 | var compressed = compress.all(directory, files, filename); 38 | return fs.writeFile(directory + "/" + newFile.trim(), compressed); 39 | } catch (x) { 40 | return false; 41 | } 42 | 43 | }); 44 | 45 | } 46 | 47 | 48 | // The Compiler 49 | // 50 | // Watches a directory for changes and compiles files 51 | // of a specific type 52 | // -------------------------------------------------- // 53 | 54 | module.exports = function compile (a) { 55 | 56 | var target = a.owner || "./" 57 | , filetype = a.subject 58 | , nodebot = this 59 | , stat; 60 | 61 | // We shouldn't compress files we don't support 62 | if (supported.indexOf(filetype) < 0) return nodebot.request("Sorry, I can't compile %s", filetype); 63 | 64 | try { 65 | stat = fs.statSync(target); 66 | } catch (err) { 67 | 68 | } 69 | 70 | // Watch directories 71 | // -------------------------------------------------- // 72 | 73 | // Is the target a directory? 74 | if (stat && stat.isDirectory()) { 75 | 76 | // Yes, watch the target 77 | fs.watch(target, { persistent: true, interval: 1000 }, function () { 78 | nodebot.say("Files have changed. Compiling all %s in %s.", filetype, target); 79 | compressor(target, filetype); 80 | }); 81 | compressor(target, filetype); 82 | nodebot.say("I am now compiling all %s in %s", filetype, target); 83 | 84 | } else { 85 | 86 | // No, use the current directory 87 | fs.watch(process.cwd(), { persistent: true, interval: 1000 }, function () { 88 | nodebot.say("Files have changed. Compiling all %s into %s.", filetype, target); 89 | compressor(process.cwd(), filetype, target); 90 | }); 91 | 92 | nodebot.say("I am now compiling all %s into %s", filetype, target); 93 | compressor(process.cwd(), filetype, target); 94 | } 95 | 96 | // Closeout 97 | // -------------------------------------------------- // 98 | 99 | compressor(target, filetype); 100 | 101 | // Make it so that when the user exits watching, it loads 102 | // up another prompt 103 | process.once('SIGINT', function () { 104 | nodebot.request("Okay, I am no longer compiling %s files.", filetype, target); 105 | }); 106 | 107 | }; 108 | -------------------------------------------------------------------------------- /actions/compile/javascript.js: -------------------------------------------------------------------------------- 1 | // Javascript Compressor 2 | // A simple module for uglifying javascripts 3 | // -------------------------------------------------- // 4 | 5 | var jsp = require("uglify-js").parser, 6 | pro = require("uglify-js").uglify, 7 | fs = require('fs') 8 | ; 9 | 10 | // Given an array, move the "from" to "to" 11 | // -------------------------------------------------- // 12 | 13 | function move (array, from, to) { 14 | array.splice(to, 0, array.splice(from, 1)[0]); 15 | }; 16 | 17 | 18 | // Get all requirements from files and sort accordingly 19 | // -------------------------------------------------- // 20 | 21 | function sortRequirements (files, directory) { 22 | 23 | var output = []; 24 | 25 | files.forEach(function (item) { 26 | 27 | var doc = item[1].split("\n\n")[0]; 28 | 29 | if (!doc) return false; 30 | 31 | var match = doc.match(/\/\/= require\s\S+(\s+|)/ig), 32 | matches = []; 33 | 34 | match && match.forEach(function(m) { 35 | var req = m.replace(/\/\/= require\s/ig, "").trim(); 36 | 37 | matches.push(directory + "/" + req); 38 | }); 39 | 40 | output.push({ 41 | filename : item[0], 42 | content : item[1], 43 | requirements : matches 44 | }); 45 | 46 | }); 47 | 48 | // For each file we output 49 | output.forEach(function(a, indexParent) { 50 | 51 | // Check each file for possible requirements 52 | output.forEach(function(b, indexChild) { 53 | 54 | // Yes? move the required child before the parent 55 | if (a.requirements.indexOf(b.filename) > -1) { 56 | move(output, indexChild, indexParent - 1); 57 | } 58 | 59 | }); 60 | 61 | }); 62 | 63 | return output; 64 | } 65 | 66 | 67 | 68 | // A simple uglifier script 69 | function compress (filename, content) { 70 | 71 | var ret = ""; 72 | 73 | content = jsp.parse(content.toString()); 74 | content = pro.ast_mangle(content); 75 | content = pro.ast_squeeze(content); 76 | 77 | // Add the records 78 | ret += "\/\/ " + filename; 79 | ret += "\n" + pro.gen_code(content) + ";\n\n"; 80 | 81 | return ret; 82 | 83 | }; 84 | 85 | 86 | // A simple uglifier script 87 | function compressAll (directory, files, filename) { 88 | 89 | var stack = [], 90 | newFile = directory + "/" + (filename || "javascript.min.js"), 91 | filter = Nodebot.lexicon.javascript["regular expression"]; 92 | 93 | // Filter down to only the non-hidden files of the type 94 | // And not the filtered file we'll create 95 | 96 | files.filter(function(f) { 97 | return f !== filename && filter.test(f); 98 | }).forEach(function(f) { 99 | try { 100 | stack.push([f, fs.readFileSync(f).toString()]); 101 | } catch (x) {} 102 | }); 103 | 104 | files = sortRequirements(stack, directory); 105 | 106 | var output = "// Uglified by Nodebot \n//\n// " + new Date().toString() 107 | + "\n\/\/" + Array(60).join("-") 108 | + "\n\n;" 109 | ; 110 | 111 | files.forEach(function(f) { 112 | output += compress(f.filename, f.content); 113 | }); 114 | 115 | return output; 116 | 117 | }; 118 | 119 | 120 | 121 | // Exports 122 | // -------------------------------------------------- // 123 | 124 | module.exports.all = compressAll; 125 | module.exports.single = compress; -------------------------------------------------------------------------------- /actions/compile/test/arms.js: -------------------------------------------------------------------------------- 1 | //= require cat.js -------------------------------------------------------------------------------- /actions/compile/test/cat.js: -------------------------------------------------------------------------------- 1 | // do something 2 | 3 | function cat () { 4 | 5 | } -------------------------------------------------------------------------------- /actions/compile/test/cat.min.js: -------------------------------------------------------------------------------- 1 | // Uglified by Nodebot 2 | // 3 | // Sun Jan 29 2012 21:56:00 GMT-0500 (EST) 4 | //----------------------------------------------------------- 5 | 6 | // /Users/nate/Sites/nodebot/actions/compile/test/cat.js 7 | function cat(){} 8 | 9 | // /Users/nate/Sites/nodebot/actions/compile/test/legs.js 10 | function legs(){} 11 | 12 | // /Users/nate/Sites/nodebot/actions/compile/test/arms.js 13 | 14 | 15 | // /Users/nate/Sites/nodebot/actions/compile/test/hands.js 16 | function hands(){} 17 | 18 | // /Users/nate/Sites/nodebot/actions/compile/test/head.js 19 | function head(){} 20 | 21 | // /Users/nate/Sites/nodebot/actions/compile/test/eyes.js 22 | function eyes(){} 23 | 24 | -------------------------------------------------------------------------------- /actions/compile/test/eyes.js: -------------------------------------------------------------------------------- 1 | // do something 2 | // 3 | //= require cat.js 4 | //= require head.js 5 | 6 | function eyes () { 7 | 8 | } -------------------------------------------------------------------------------- /actions/compile/test/hands.js: -------------------------------------------------------------------------------- 1 | //= require arms.js 2 | //= require cat.js 3 | 4 | function hands () { 5 | 6 | } -------------------------------------------------------------------------------- /actions/compile/test/head.js: -------------------------------------------------------------------------------- 1 | // do something 2 | //= require cat.js 3 | 4 | 5 | function head () { 6 | 7 | } -------------------------------------------------------------------------------- /actions/compile/test/legs.js: -------------------------------------------------------------------------------- 1 | //= require cat.js 2 | 3 | function legs () { 4 | 5 | } -------------------------------------------------------------------------------- /actions/how.js: -------------------------------------------------------------------------------- 1 | // How 2 | // Basically an alias for "what" 3 | // -------------------------------------------------- // 4 | 5 | module.exports = function how (a) { 6 | 7 | // Do we have success? 8 | if (a.owner && a.subject) { 9 | return this.actions.what.apply(this, [a]); 10 | } 11 | 12 | this.say("I don't know how to %s", a.subject); 13 | return this.request(); 14 | 15 | }; -------------------------------------------------------------------------------- /actions/index.js: -------------------------------------------------------------------------------- 1 | // Actions 2 | // 3 | // Tells the nodebot how to do a command 4 | // -------------------------------------------------- // 5 | 6 | var actions = {}; 7 | 8 | // Who 9 | actions.who = require("./who"); 10 | 11 | // Relabel 12 | // actions.relabel = actions.set = require("./relabel"); 13 | 14 | // What 15 | actions.what = require("./what"); 16 | 17 | // Validate, Wrong 18 | actions.validate = actions.wrong = require("./validate"); 19 | 20 | // Watch, Look 21 | actions.watch = actions.look = require("./watch"); 22 | 23 | // Compile 24 | actions.compile = actions.compress = require("./compile"); 25 | 26 | // How 27 | actions.how = require("./how"); 28 | 29 | actions.repeat = actions.again = require("./repeat"); 30 | 31 | // Export it all out 32 | // -------------------------------------------------- // 33 | 34 | module.exports = actions; -------------------------------------------------------------------------------- /actions/relabel.js: -------------------------------------------------------------------------------- 1 | // Relabel 2 | // -------------------------------------------------- // 3 | 4 | var lang = require("../brain/language"); 5 | 6 | module.exports = function relabel (a) { 7 | 8 | var nodebot = this 9 | , ownership = a.owner 10 | , subject = a.subject 11 | , description = lang.capitalize(a.description) 12 | ; 13 | 14 | console.log(a); 15 | 16 | if (ownership === subject) { 17 | subject = "definition"; 18 | } 19 | 20 | // Standard bullet proofing 21 | nodebot.lexicon[ownership] = nodebot.lexicon[ownership] || {}; 22 | 23 | nodebot.ask("Just to confirm," + " you said that " + lang.possessify(ownership) + " " + a.subject + " " + a.keyverb + " " + description.green.bold + "? (y/n)", function(data) { 24 | 25 | if (data[0].toLowerCase() === "y") { 26 | nodebot.say("Okay, thanks! Now I know that " + (lang.possessify(ownership) + " " + subject + " is " + description).green.bold); 27 | nodebot.lexicon[ownership][subject] = description; 28 | } else { 29 | nodebot.say("I must be confused, sorry."); 30 | } 31 | 32 | return nodebot.request(); 33 | 34 | }); 35 | 36 | }; 37 | -------------------------------------------------------------------------------- /actions/repeat.js: -------------------------------------------------------------------------------- 1 | // Repeat actions 2 | // -------------------------------------------------- // 3 | 4 | module.exports = function repeat () { 5 | 6 | var action = ""; 7 | 8 | if (this.memory.tasks === []) { 9 | this.say("I haven't done anything yet."); 10 | return this.request(); 11 | } else { 12 | action = this.memory.tasks.slice(-1).toString(); 13 | } 14 | 15 | // Run the old action 16 | return this.analyze(action); 17 | 18 | }; -------------------------------------------------------------------------------- /actions/uglify.js: -------------------------------------------------------------------------------- 1 | // Uglify 2 | // -------------------------------------------------- // 3 | 4 | var fs = require('fs'); 5 | 6 | // Scanners 7 | var urlEx = Nodebot.lexicon.url['regular expression'] 8 | , fileEx = Nodebot.lexicon.file['regular expression'] 9 | ; 10 | 11 | var supported = ['javascript']; 12 | 13 | module.exports = function uglify (a) { 14 | 15 | var directory = a.owner || "./" 16 | , filetype = a.subject 17 | , nodebot = this 18 | , stat; 19 | 20 | console.log(a); 21 | 22 | // Bullet proof websites 23 | if (directory.match(urlEx)) return nodebot.request("Sorry, I can't compile websites"); 24 | 25 | try { 26 | stat = fs.statSync(directory); 27 | } catch (err) { 28 | nodebot.say("Hmm, I couldn't find %s. Does it exist?", directory); 29 | return nodebot.request(); 30 | } 31 | 32 | // Watch directories 33 | // -------------------------------------------------- // 34 | 35 | if (stat.isDirectory()) { 36 | 37 | fs.watch(directory, { persistent: true, interval: 1000 }, function (event, filename) { 38 | nodebot.say("Looks like something has changed in " + directory.blue.bold); 39 | }); 40 | 41 | } 42 | 43 | // Closeout 44 | // -------------------------------------------------- // 45 | 46 | nodebot.say("I am now compiling all %s in %s", filetype.blue.bold, directory.blue); 47 | 48 | // Make it so that when the user exits watching, it loads 49 | // up another prompt 50 | process.once('SIGINT', function () { 51 | nodebot.request("Okay, I am no longer watching", directory.blue.bold); 52 | }); 53 | 54 | }; 55 | -------------------------------------------------------------------------------- /actions/validate.js: -------------------------------------------------------------------------------- 1 | // Validate 2 | // -------------------------------------------------- // 3 | 4 | var fs = require('fs'); 5 | 6 | // Scanners 7 | var urlEx = Nodebot.lexicon.url['regular expression'] 8 | , fileEx = Nodebot.lexicon.file['regular expression'] 9 | , jsEx = Nodebot.lexicon.javascript['regular expression'] 10 | , cssEx = Nodebot.lexicon.css['regular expression'] 11 | , htmlEx = Nodebot.lexicon.html['regular expression'] 12 | ; 13 | 14 | 15 | module.exports = function validate (a, skipRequest) { 16 | 17 | var target = a.subject.split(" ").join("") 18 | , nodebot = this; 19 | 20 | if (!target.match(urlEx)) { 21 | 22 | try { 23 | fs.statSync(target); 24 | } catch (err) { 25 | nodebot.say("I had trouble finding " + target + ". Does it exist?".cyan); 26 | 27 | return nodebot.request(); 28 | } 29 | } 30 | 31 | function dispatch(fileType) { 32 | require("../validator/" + fileType).validate.apply(nodebot, [target, function cb() { 33 | return (skipRequest) ? false : nodebot.request(); 34 | }]); 35 | } 36 | 37 | // VALIDATORS 38 | // -------------------------------------------------- // 39 | 40 | // Javascript 41 | if (target.match(jsEx)) dispatch("javascript"); 42 | 43 | // CSS 44 | else if (target.match(cssEx)) dispatch("css"); 45 | 46 | // Websites 47 | else if (target.match(urlEx)) dispatch("website"); 48 | 49 | // HTML 50 | else if (target.match(htmlEx)) dispatch("html"); 51 | 52 | // Elsecase 53 | else { 54 | this.say("I'm sorry, I don't recognize that file format yet"); 55 | if (!skipRequest) return nodebot.request(); 56 | } 57 | 58 | }; 59 | -------------------------------------------------------------------------------- /actions/watch.js: -------------------------------------------------------------------------------- 1 | // Watch 2 | // -------------------------------------------------- // 3 | 4 | var fs = require('fs') 5 | , os = require('os'); 6 | 7 | // Scanners 8 | var urlEx = Nodebot.lexicon.url['regular expression'] 9 | , fileEx = Nodebot.lexicon.file['regular expression'] 10 | , jsEx = Nodebot.lexicon.javascript['regular expression'] 11 | , cssEx = Nodebot.lexicon.css['regular expression'] 12 | , htmlEx = Nodebot.lexicon.html['regular expression'] 13 | ; 14 | 15 | module.exports = function watch (a) { 16 | 17 | var file = a.subject || "./" 18 | , nodebot = this 19 | , stat; 20 | 21 | // Bullet proof websites 22 | if (file.match(urlEx)) { 23 | this.say("This is a website. I don't know how to watch these yet, so I'll pass it through the web validator"); 24 | return nodebot.actions.validate.apply(this, [a]); 25 | } 26 | 27 | try { 28 | stat = fs.statSync(file); 29 | } catch (err) { 30 | nodebot.say("I couldn't find that file, does it exist?"); 31 | return nodebot.request(); 32 | } 33 | 34 | // Watch files 35 | // -------------------------------------------------- // 36 | 37 | stat.isFile() && fs.watchFile(file, { persistent: true, interval: 1000 }, function (curr, prev) { 38 | 39 | var content = fs.readFileSync(file, "utf-8"); 40 | 41 | if (content !== nodebot.memory.watched_content) { 42 | nodebot.say("Looks like something has changed on " + file.blue.bold); 43 | nodebot.actions.validate.apply(nodebot, [a, true]); 44 | 45 | // Update with new content 46 | nodebot.memory.watched_content = content; 47 | } 48 | }); 49 | 50 | 51 | // Watch directories 52 | // -------------------------------------------------- // 53 | 54 | if (stat.isDirectory()) { 55 | 56 | // Are we dealing with OSX? 57 | switch(os.type()) { 58 | 59 | case "Darwin": 60 | return nodebot.say("Node v%s doesn't have good fs.watch support on %s, I'm sorry :(", process.versions.node, "OSX".cyan); 61 | break; 62 | 63 | default: 64 | fs.watch(file, { persistent: true, interval: 1000 }, function (event, filename) { 65 | 66 | if (!filename) return false; 67 | 68 | var content = fs.readFileSync(filename, "utf-8"); 69 | 70 | if (content !== nodebot.memory.watched_content) { 71 | nodebot.say("Looks like something has changed on " + file.blue.bold); 72 | actions.validate.apply(nodebot, [a, true]); 73 | 74 | // Update with new content 75 | nodebot.memory.watched_content = content; 76 | } 77 | }); 78 | } 79 | 80 | } 81 | 82 | 83 | // Closeout 84 | // -------------------------------------------------- // 85 | 86 | nodebot.say("I am now watching " + file.blue.bold); 87 | 88 | // Make it so that when the user exits watching, it loads 89 | // up another prompt 90 | process.once('SIGINT', function () { 91 | nodebot.say("I am no longer watching " + file.blue.bold); 92 | nodebot.request(); 93 | }); 94 | 95 | }; 96 | -------------------------------------------------------------------------------- /actions/what.js: -------------------------------------------------------------------------------- 1 | // What.js 2 | // "What is your name?", 3 | // "What is the capital of Spain?" 4 | // -------------------------------------------------- // 5 | 6 | var lang = require("../brain/language") 7 | , fileEx = Nodebot.lexicon.file["regular expression"] 8 | , request = require("request") 9 | , format = require("../brain/formatter") 10 | ; 11 | 12 | 13 | module.exports = function what (a) { 14 | 15 | if (a.owner === a.subject) a.subject = "definition"; 16 | 17 | var nodebot = this 18 | , owner = a.owner 19 | , subject = a.subject || "definition" 20 | 21 | , base = (owner) ? nodebot.lexicon[owner] : nodebot.lexicon 22 | , term = (subject && base) ? base[subject] : undefined 23 | ; 24 | 25 | // Just some more bullet proofing for the subject 26 | subject = (owner === subject) ? "definition" : subject; 27 | 28 | // If the term is a function, call it to determin the value 29 | if (typeof term === "function") term = term().toString(); 30 | 31 | // If it is a file, simply return that it is a file 32 | if (fileEx.test(subject)) { 33 | return nodebot.say(owner.cyan + " is a file.").request(); 34 | } 35 | 36 | 37 | // Do we have a definition for this subject? 38 | // -------------------------------------------------- // 39 | 40 | if (term) { 41 | 42 | switch (subject) { 43 | 44 | case "definition": 45 | nodebot.say(lang.capitalize(owner) + " is " + term.green.bold); 46 | break; 47 | default: 48 | nodebot.say(lang.possessify(lang.capitalize(owner)) + " " + subject + " is " + term.toString().green.bold); 49 | } 50 | 51 | return nodebot.request(); 52 | 53 | } 54 | 55 | 56 | // No, search WolframAlpha 57 | // 58 | // For more information visit: 59 | // http://www.wolframalpha.com/termsofuse.html#attributionandlicensing 60 | // -------------------------------------------------- // 61 | 62 | var app_id = "GVH84U-9YQ66P7PU3"; 63 | 64 | var qs = require('querystring') 65 | , sax = require("../sax-js") 66 | , strict = true 67 | , parser = sax.parser(strict) 68 | , request = require("request") 69 | , data = qs.stringify({ input: a.tokens.join(" ") }) 70 | ; 71 | 72 | nodebot.say("Hmm, I don't know off the top of my head. Let me ask around..."); 73 | 74 | request.get("http://api.wolframalpha.com/v2/query?" + data + "&appid=" + app_id, function(err, data) { 75 | 76 | var result = []; 77 | 78 | parser.ontext = function(t) { 79 | var proc = t.trim(); 80 | if (proc !== "" && proc !== "\n") result.push(proc); 81 | }; 82 | 83 | parser.onend = function () { 84 | 85 | // If nothing was found, then we have an issue 86 | // -------------------------------------------------- // 87 | 88 | if (result.length === 0) { 89 | nodebot.say("I couldn't find anything, sorry :("); 90 | return nodebot.request(); 91 | } 92 | 93 | nodebot.say("Here's what I found:\n"); 94 | 95 | var tidbit = []; 96 | 97 | format.drawLine(); 98 | console.log(format.align(result[0], 80).bold.red); 99 | format.drawLine(); 100 | 101 | console.log(""); 102 | 103 | var message = ""; 104 | 105 | result = result.filter(function(i) { return i.trim() !== ""; }); 106 | 107 | result.slice(1, 4).forEach(function(i) { 108 | 109 | if (i.split("|").length > 1) { 110 | message += "\n" + format.clump(i.split("|").join(": ")); 111 | 112 | } else { 113 | var tmp = i.split("\n").join(". ").trim(); 114 | message += "\n\n" + format.clump(tmp, 76); 115 | } 116 | 117 | }); 118 | 119 | var credit = "Courtesy of " + "WolframAlpha" + " (http://www.wolframalpha.com)"; 120 | 121 | nodebot.io && nodebot.io.sockets.emit('output', message); 122 | nodebot.io && nodebot.io.sockets.emit('output', credit); 123 | 124 | console.log(""); 125 | 126 | format.drawLine(); 127 | console.log(format.align(credit, 80)); 128 | format.drawLine(); 129 | 130 | return nodebot.request(); 131 | 132 | }; 133 | 134 | parser.write(data.body).close(); 135 | 136 | 137 | }); 138 | 139 | }; 140 | -------------------------------------------------------------------------------- /actions/who.js: -------------------------------------------------------------------------------- 1 | // Who 2 | // -------------------------------------------------- // 3 | 4 | var lang = require("../brain/language"); 5 | 6 | module.exports = function who(a) { 7 | 8 | var nodebot = this 9 | , owner = a.owner 10 | , key = a.subject 11 | ; 12 | 13 | // Special instances 14 | // -------------------------------------------------- // 15 | switch(owner){ 16 | 17 | case "nodebot": 18 | 19 | if (this.lexicon.nodebot[key]) { 20 | this.say("My " + key + " is " + this.lexicon.nodebot[key]); 21 | } else { 22 | this.say(this.lexicon.nodebot.name + " is " + this.lexicon.nodebot.definition); 23 | } 24 | 25 | return this.request(); 26 | 27 | case "user": 28 | 29 | if (this.lexicon.user[key]) { 30 | this.say("You are my master, " + this.lexicon.user.name); 31 | } else { 32 | this.say("Hmm... I can't remember, " + this.lexicon.user.name); 33 | } 34 | return this.request(); 35 | } 36 | 37 | 38 | // Typical cases 39 | // -------------------------------------------------- // 40 | 41 | if (nodebot.lexicon[owner] !== undefined) { 42 | this.say(lang.capitalize(owner) + " is " + this.lexicon[owner][key].green.bold); 43 | return this.request(); 44 | } 45 | 46 | 47 | return nodebot.actions.what.apply(nodebot, [a]); 48 | 49 | 50 | }; 51 | -------------------------------------------------------------------------------- /bin/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhunzaker/nodebot/fa3fc9d65475214ac3bc72548004d5eef63b0e25/bin/.DS_Store -------------------------------------------------------------------------------- /bin/classifier.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhunzaker/nodebot/fa3fc9d65475214ac3bc72548004d5eef63b0e25/bin/classifier.json -------------------------------------------------------------------------------- /bin/lexicon.json: -------------------------------------------------------------------------------- 1 | {"you":"Lexicon","bird":"flying creatures","me":"Nate","app.j":"no","it":"no"} -------------------------------------------------------------------------------- /bin/nodebot: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require("../nodebot.js"); 4 | 5 | Nodebot.boot(process.argv[2] === "-s"); -------------------------------------------------------------------------------- /brain/analyze.js: -------------------------------------------------------------------------------- 1 | // The A Module 2 | // -------------------------------------------------- // 3 | 4 | var lang = require("./language"), 5 | n = Nodebot; 6 | 7 | module.exports = function (data) { 8 | 9 | var a = lang.classify(data); 10 | 11 | // let's filter the ownership 12 | switch(a.owner) { 13 | 14 | // Reverse user possession 15 | case "speaker": 16 | a.owner = "user"; 17 | break; 18 | 19 | // Reverse nodebot possession 20 | case "listener": 21 | a.owner = "nodebot"; 22 | break; 23 | 24 | // Tweak other non-specific possession cases to the last 25 | // recorded context 26 | case "it": case "its": case "they": case "their": 27 | case "he": case "she": case "his": case "hers": 28 | a.owner = Nodebot.memory.context; 29 | break; 30 | } 31 | 32 | 33 | // If there is no action, but the owner is the user or the robot, it 34 | // is a relabeling 35 | 36 | if (!a.action || (!a.owner && a.subject === "")) { 37 | n.say("I'm not sure what you are asking me to do, please clarify"); 38 | return n.request(); 39 | } 40 | 41 | // Now, let's also figure out the best action to take based upon 42 | // what the nodebot can actually do 43 | var action = lang.closest(a.action, Object.keys(this.actions)); 44 | 45 | // Unless we are repeating the action, store it for later recollection 46 | if (action !== "repeat") { 47 | n.memory.tasks.push(data); 48 | n.memory.context = a.ownership; 49 | } 50 | 51 | return n.actions[action].apply(n, [a]); 52 | 53 | }; -------------------------------------------------------------------------------- /brain/formatter.js: -------------------------------------------------------------------------------- 1 | 2 | // Formatter.js 3 | // 4 | // Provides some helper functions 5 | 6 | module.exports.align = function align (string, width, orientation) { 7 | 8 | var spacer = "" 9 | , count = 0 10 | ; 11 | 12 | // We need to make sure the width is long enough 13 | width = (width > string.length) ? width : string.length; 14 | 15 | switch(orientation) { 16 | 17 | case "left": 18 | count = Math.round(width - string.length + 1); 19 | spacer = Array(count).join(" "); 20 | return string + spacer; 21 | 22 | case "right": 23 | count = Math.round((width - string.length) + 1); 24 | spacer = Array(count).join(" "); 25 | return spacer + string; 26 | 27 | case "center": default: 28 | count = Math.round(((width / 2) - (string.length / 2)) + 1); 29 | spacer = Array(count).join(" "); 30 | return spacer + string + spacer; 31 | 32 | } 33 | 34 | }; 35 | 36 | 37 | 38 | // Draws a horizontal line, given a length 39 | // -------------------------------------------------- // 40 | 41 | module.exports.drawLine = function drawLine (length) { 42 | console.log(Array(length || 80).join("-")); 43 | } 44 | 45 | 46 | 47 | // Keeps lines at a specific word count 48 | // and keeps them in proper left alignment 49 | // -------------------------------------------------- // 50 | 51 | module.exports.clump = function clump (words, limit, lineOffset) { 52 | 53 | limit = limit || 80; 54 | lineOffset = lineOffset || 0; 55 | 56 | var clump = [] 57 | , offset = "" 58 | , line = [] 59 | ; 60 | 61 | // Run through a loop of all words 62 | words.split(" ").forEach(function(word) { 63 | 64 | // Get the length of the current line, which is: 65 | // the line offset + the current line + the new word 66 | var length = lineOffset + line.join(" ").length + word.length; 67 | 68 | // Is the length of the new line greater than the limit? 69 | if (length <= limit) { 70 | 71 | // no : push the new word into the current line 72 | line.push(word.trim()); 73 | 74 | } else { 75 | 76 | // yes : push the line in to the clump and reset 77 | // the line 78 | clump.push(line.join(" ")); 79 | line = [word]; 80 | } 81 | 82 | }); 83 | 84 | // This process skips the last line, so let's add it now: 85 | clump.push(line.join(" ")); 86 | 87 | 88 | // Now, create the left margin by compressing an array 89 | // into a string 90 | offset = Array(lineOffset).join(" "); 91 | 92 | // Filter whitespace and pull everthing together 93 | clump = clump.map(function(c) { 94 | return c.trim() 95 | }).join("\n" + offset).slice(0, -1); 96 | 97 | return clump; 98 | 99 | }; 100 | 101 | 102 | 103 | // A whitespace generator 104 | // Creates a blank Array and stitches it together 105 | // -------------------------------------------------- // 106 | 107 | module.exports.whitespace = function whitespace (length, max) { 108 | 109 | max = max || 12; 110 | 111 | var num = Math.abs(12 - (length || 0)) 112 | , space = Array(num); 113 | 114 | return space.join(" "); 115 | 116 | } 117 | -------------------------------------------------------------------------------- /brain/interaction.js: -------------------------------------------------------------------------------- 1 | // The Analysis Module 2 | // -------------------------------------------------- // 3 | 4 | var growl = require('growl'), 5 | util = require("util"); 6 | 7 | module.exports = function add_interaction_module(context) { 8 | 9 | var nodebot = Nodebot; 10 | 11 | // Handle Prompts 12 | // -------------------------------------------------- // 13 | 14 | nodebot.ask = function ask(question, callback) { 15 | var stdin = process.stdin 16 | , stdout = process.stdout; 17 | 18 | stdout.write("\n" + this.lexicon.nodebot.name.magenta.bold + ": " + question + "\n Response: ".blue.bold); 19 | stdin.resume(); 20 | 21 | stdin.once('data', function(data) { 22 | callback(data.toString()); 23 | }); 24 | }; 25 | 26 | nodebot.request = function() { 27 | 28 | if (arguments) nodebot.say.apply(nodebot, arguments); 29 | 30 | var statement = "What can I help you with?"; 31 | nodebot.ask(statement, function(command) { 32 | nodebot.analyze(command); 33 | }); 34 | }; 35 | 36 | nodebot.say = function() { 37 | 38 | var message = util.format.apply(null, arguments); 39 | 40 | if (nodebot.io) { 41 | nodebot.io.sockets.emit('output', message); 42 | } else { 43 | console.log(nodebot.lexicon.nodebot.name.magenta.bold + ": " + message); 44 | } 45 | 46 | return this; 47 | }; 48 | 49 | nodebot.growl = function (message, type) { 50 | growl(message, { title: 'NodeBot:' }); 51 | 52 | return this; 53 | }; 54 | 55 | }; -------------------------------------------------------------------------------- /brain/language/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhunzaker/nodebot/fa3fc9d65475214ac3bc72548004d5eef63b0e25/brain/language/.DS_Store -------------------------------------------------------------------------------- /brain/language/index.js: -------------------------------------------------------------------------------- 1 | // The Language Module 2 | // -------------------------------------------------- // 3 | 4 | var speak = require("speakeasy-nlp"); 5 | var lang = {}; 6 | 7 | lang.classify = speak.classify; 8 | lang.closest = speak.closest; 9 | 10 | lang.possessify = function (str) { 11 | 12 | if (!lang.isPossessive(str) && str !== "") { 13 | str = str + (str.slice(-1).toLowerCase() === "s" ? "'" : "'s"); 14 | } 15 | 16 | return str; 17 | 18 | }; 19 | 20 | lang.depossessify = function (string) { 21 | 22 | var str = string; 23 | 24 | if (lang.isPossessive(str)) { 25 | 26 | if (str.match("'s")) { 27 | str = str.slice(0, -2); 28 | } else { 29 | str = str.slice(0, -1); 30 | } 31 | 32 | } 33 | 34 | return str; 35 | }; 36 | 37 | lang.isPossessive = function (str) { 38 | str = str.slice(-2); 39 | 40 | return (str === "'s" || str === "s'"); 41 | }; 42 | 43 | lang.capitalize = function (string) { 44 | if (typeof string !== 'string') return string; 45 | return string[0].toUpperCase() + string.slice(1); 46 | }; 47 | 48 | module.exports = lang; -------------------------------------------------------------------------------- /brain/lexicon.js: -------------------------------------------------------------------------------- 1 | 2 | // The Nodebot Lexicon 3 | // -------------------------------------------------- // 4 | 5 | var lexicon = {} 6 | , os = require("os") 7 | , home = process.cwd() 8 | , system = os.type().replace("Darwin", "OSX") + os.release() + "(" + os.arch() + ")" 9 | ; 10 | 11 | 12 | module.exports = lexicon = { 13 | 14 | nodebot: { 15 | 16 | definition: "a robot, it lives to serve.", 17 | 18 | name: "Nodebot", 19 | 20 | 'favorite color': "green".green.bold, 21 | 22 | birthday: new Date().toString(), 23 | 24 | time : function() { 25 | return new Date().toString(); 26 | } 27 | }, 28 | 29 | user: { 30 | 31 | name : "Master", 32 | 33 | definition : "my master.", 34 | 35 | "current directory" : home, 36 | 37 | "operating system" : system, 38 | 39 | "host name" : os.hostname(), 40 | 41 | "free memory" : (os.freemem() / 1000000) + "MB", 42 | 43 | 'ip address' : function() { 44 | 45 | var os = require('os') 46 | , ifaces = os.networkInterfaces() 47 | , addresses = "" 48 | ; 49 | 50 | for (var dev in ifaces) { 51 | 52 | ifaces[dev].forEach(function(details){ 53 | if (details.family === 'IPv4') { 54 | addresses += "\n" + ((details.internal) ? " local - " : "external - ") + details.address; 55 | } 56 | }); 57 | } 58 | 59 | return "\n" + addresses; 60 | 61 | }, 62 | 63 | 'ip addresses' : function() { 64 | return self.lexicon.user['ip address'](); 65 | } 66 | 67 | }, 68 | 69 | 'up' : { 70 | definition: "What's up? I'm not really sure, it's so hard to explain to " 71 | + "4 dimensional beings" 72 | }, 73 | 74 | 75 | // OS Lex 76 | // ---------- // 77 | 78 | "current directory" : { 79 | definition: home 80 | }, 81 | 82 | "operating system" : { 83 | definition: system 84 | }, 85 | 86 | "time": { 87 | definition: function() { 88 | return new Date().toString(); 89 | } 90 | }, 91 | 92 | // Regular expression related items 93 | // -------------------------------------------------- // 94 | 95 | "email": { 96 | definition : "Messages sent across the internet", 97 | "regular expression" : /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\.+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ 98 | }, 99 | 100 | "url": { 101 | definition : "The address for a website or location on the internet", 102 | "regular expression" : /\b(?:(?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\((?:[^\s()<>]+|(?:\([^\s()<>]+\)))*\))+(?:\((?:[^\s()<>]+|(?:\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“”‘’]))/ 103 | }, 104 | 105 | "file": { 106 | definition : "A place where information is stored on a computer", 107 | "regular expression" : /\S+\.\S+[^\/\?]/ 108 | }, 109 | 110 | "javascript": { 111 | definition : "The language of the internet", 112 | "regular expression" : /\.js$/, 113 | mime : ".js" 114 | }, 115 | 116 | "css": { 117 | definition : "The great beautifier of the internet", 118 | "regular expression" : /\.css$/, 119 | mime : ".css" 120 | }, 121 | 122 | "sass": { 123 | definition : "Syntaxually awesome stylesheets", 124 | "regular expression" : /\.sass$/, 125 | mime : ".sass" 126 | }, 127 | 128 | "scss": { 129 | definition : "Syntaxually awesome stylesheets", 130 | "regular expression" : /\.scss$/, 131 | mime : ".scss" 132 | }, 133 | 134 | "html": { 135 | definition : "The great information organizer of the internet", 136 | "regular expression" : /\.html$/, 137 | mime : ".html" 138 | } 139 | 140 | }; 141 | 142 | -------------------------------------------------------------------------------- /brain/time/index.js: -------------------------------------------------------------------------------- 1 | 2 | // The Time Module 3 | // -------------------------------------------------- // 4 | 5 | var moment = require("moment"); 6 | 7 | module.exports = function time () { 8 | 9 | this.tomorrow = moment().add("days", 1); 10 | 11 | }; -------------------------------------------------------------------------------- /nodebot.js: -------------------------------------------------------------------------------- 1 | // Nodebot.js 2 | // 3 | // Description: A helper robot that lives to serve 4 | // Author: Nate Hunzaker 5 | // -------------------------------------------------- // 6 | // Licence: MIT 7 | // -------------------------------------------------- // 8 | //-- require application.js 9 | //-- require nodebot.js 10 | 11 | 12 | require('colors'); 13 | 14 | // Get the initial action 15 | var command = process.argv.slice(2).join(" ").trim(); 16 | 17 | // -------------------------------------------------- // 18 | 19 | Nodebot = new(require("events").EventEmitter)(); 20 | 21 | // short term memory 22 | Nodebot.memory = { 23 | tasks : [], 24 | context : "nodebot" 25 | }; 26 | 27 | // long term memory 28 | Nodebot.lexicon = require("./brain/lexicon"); 29 | 30 | // The linguistics module 31 | Nodebot.language = require("./brain/language"); 32 | 33 | // All actions the nodebot can take 34 | Nodebot.actions = require("./actions"); 35 | 36 | // Adds the decision making module 37 | Nodebot.analyze = require("./brain/analyze"); 38 | 39 | // Adds common interactions, such as say, growl, ask and request 40 | require("./brain/interaction")(Nodebot); 41 | 42 | // -------------------------------------------------- // 43 | 44 | Nodebot.boot = function(server) { 45 | 46 | if (server) { 47 | var app = require("./server"); 48 | } else { 49 | (command !== "") ? Nodebot.analyze(command) : Nodebot.request(); 50 | } 51 | 52 | }; 53 | 54 | // Take the proper initial action 55 | 56 | if (!module.parent) { 57 | 58 | process.on("exit", function() { 59 | console.log(""); 60 | }); 61 | 62 | Nodebot.boot(command === "-s"); 63 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodebot", 3 | "description": "An artificial intelligence written for NodeJS", 4 | "version": "0.1.7", 5 | 6 | "homepage": "", 7 | 8 | "engines": { 9 | "node": ">=0.6.4" 10 | }, 11 | 12 | "dependencies": { 13 | 14 | "csslint" : "*", 15 | "colors" : "*", 16 | "growl" : "*", 17 | "jquery" : "*", 18 | "jshint" : "*", 19 | "request" : "*", 20 | "speakeasy-nlp" : "*", 21 | "flatiron" : "*", 22 | "connect" : "*", 23 | "socket.io" : "*", 24 | "union" : "*", 25 | "plates" : "*" 26 | }, 27 | 28 | "author": "Nate Hunzaker", 29 | 30 | "main": "./nodebot.js", 31 | "bin" : { "nodebot": "./bin/nodebot" } 32 | } 33 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | #Nodebot 2 | 3 | A helper robot written for the busy developer. Nodebot can (currently): 4 | 5 | - Compile javascripts using Uglify.js (And all javascripts in a directory) 6 | - Validate html (websites too), css, and javascript 7 | - Watch files for changes and validate them there is a supported validator 8 | - Report system information such as ip addresses, free memory, and host names 9 | - Query Wolfram|Alpha for definitions and answers to complex questions. 10 | 11 | --- 12 | 13 | ``` 14 | npm install nodebot -g 15 | ``` 16 | 17 | ### Example commands 18 | 19 | ``` 20 | $ nodebot What is the capital of Spain? 21 | 22 | $ nodebot is nodebot.js valid? 23 | 24 | $ nodebot watch ./nodebot.js 25 | 26 | $ nodebot how much free memory do I have? 27 | ``` 28 | 29 | ### Validate Javascript, CSS, and HTML (websites work too) 30 | ``` 31 | $ nodebot is nodebot.js valid? 32 | 33 | Nodebot: Oh snap! I found 1 error in nodebot.js: 34 | ---------------------------------- 35 | 48:12 - Don't make functions within a loop. 36 | 37 | ``` 38 | 39 | 40 | ### General Information 41 | ``` 42 | $ nodebot 43 | 44 | Nodebot: What can I help you with? 45 | Response: what is the current directory 46 | 47 | Nodebot: The current directory is /Users/nate/Sites/nodebot 48 | 49 | Nodebot: What can I help you with? 50 | Response: what is my ip address? 51 | 52 | Nodebot: User's ip address is 53 | 54 | local - 127.0.0.1 55 | external - 172.30.9.66 56 | 57 | Nodebot: What can I help you with? 58 | Response: What is your name? 59 | 60 | Nodebot: Nodebot's name is Nodebot 61 | 62 | ``` 63 | 64 | ### Compiling javascript 65 | 66 | ``` 67 | $ nodebot compile all javascript as min.js 68 | 69 | Nodebot: I am now compiling all javascript into min.js 70 | 71 | Nodebot: Files have changed. Compiling all javascript into min.js. 72 | ``` 73 | 74 | -------------------------------------------------------------------------------- /server/index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 |