| 1 | var request = require('request');
|
||
| 2 | var express = require('express');
|
||
| 3 | var FeedParser = require('feedparser');
|
||
| 4 | var extend = require('extend');
|
||
| 5 | var ent = require('ent');
|
||
| 6 | |||
| 7 | var USERAGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.75 Safari/537.1"; |
||
| 8 | |||
| 9 | // ------------------------------------------ |
||
| 10 | // CONSTRUCTOR |
||
| 11 | // ------------------------------------------ |
||
| 12 | |||
| 13 | var init = function(){
|
||
| 14 | info('Starting SARAH ...');
|
||
| 15 | global.SARAH = SARAH; |
||
| 16 | |||
| 17 | SARAH.ConfigManager = require('./config.js').init();
|
||
| 18 | SARAH.PluginManager = require('./plugin.js').init();
|
||
| 19 | SARAH.LangManager = require('./lang.js').init();
|
||
| 20 | SARAH.PrivacyManager = require('./privacy.js').init();
|
||
| 21 | SARAH.PortalManager = require('./portal.js').init();
|
||
| 22 | SARAH.ScriptManager = require('./script.js').init();
|
||
| 23 | SARAH.RuleEngine = require('./rules.js').init();
|
||
| 24 | SARAH.CRONManager = require('./cron.js').init();
|
||
| 25 | SARAH.ProfileManager = require('./profile.js').init();
|
||
| 26 | SARAH.Marketplace = require('./marketplace.js').init();
|
||
| 27 | |||
| 28 | /* |
||
| 29 | SARAH.PhantomManager = require('./phantom.js').init();
|
||
| 30 | */ |
||
| 31 | |||
| 32 | SARAH.run = SARAH.ScriptManager.run; |
||
| 33 | SARAH.call = SARAH.ScriptManager.call; |
||
| 34 | SARAH.last = SARAH.ScriptManager.last; |
||
| 35 | SARAH.find = SARAH.PluginManager.find; |
||
| 36 | SARAH.exists = SARAH.PluginManager.exists; |
||
| 37 | SARAH.trigger = SARAH.PluginManager.trigger; |
||
| 38 | SARAH.listen = SARAH.PluginManager.listen; |
||
| 39 | |||
| 40 | return SARAH; |
||
| 41 | } |
||
| 42 | |||
| 43 | // ------------------------------------------ |
||
| 44 | // RSS |
||
| 45 | // ------------------------------------------ |
||
| 46 | |||
| 47 | var RSSFeedCache = {};
|
||
| 48 | var getRSSFeed = function(url, cache){
|
||
| 49 | |||
| 50 | // Use cache |
||
| 51 | if (!cache && RSSFeedCache[url]){ return RSSFeedCache[url]; }
|
||
| 52 | |||
| 53 | var feed = { items : [] };
|
||
| 54 | request(url) |
||
| 55 | .pipe(new FeedParser()) |
||
| 56 | .on('meta', function (meta) { feed.meta = meta; })
|
||
| 57 | .on('readable', function() {
|
||
| 58 | var stream = this, item; |
||
| 59 | while (item = stream.read()) {
|
||
| 60 | item.description = ent.decode(item.description); |
||
| 61 | feed.items.push(item); |
||
| 62 | } |
||
| 63 | RSSFeedCache[url] = feed; // Cache |
||
| 64 | }); |
||
|
0 ignored issues
–
show
Best Practice
introduced
by
Loading history...
|
|||
| 65 | } |
||
| 66 | |||
| 67 | // ------------------------------------------ |
||
| 68 | // ASKME |
||
| 69 | // ------------------------------------------ |
||
| 70 | |||
| 71 | var ASKME = false; |
||
| 72 | var stack = []; |
||
| 73 | |||
| 74 | var end = function(){
|
||
| 75 | ASKME = false; next(); |
||
| 76 | } |
||
| 77 | |||
| 78 | var next = function(){
|
||
| 79 | if (stack.length <= 0){
|
||
| 80 | return SARAH.context('default');
|
||
| 81 | } |
||
| 82 | var args = stack.shift(); |
||
| 83 | askme(args[0], args[1], args[2], args[3]) |
||
|
0 ignored issues
–
show
|
|||
| 84 | } |
||
| 85 | |||
| 86 | var askme = function(tts, grammar, timeout, callback, wrong){
|
||
| 87 | if (!grammar) { return; }
|
||
| 88 | if (!callback){ return; }
|
||
| 89 | if (ASKME) { return stack.push(arguments); }
|
||
| 90 | |||
| 91 | // Build request |
||
| 92 | info('AskMe', ASKME);
|
||
| 93 | ASKME = { 'sentences':[], 'tags':[] }
|
||
| 94 | if (tts){
|
||
| 95 | ASKME.tts = tts; |
||
| 96 | ASKME.sync = true; |
||
| 97 | ASKME.wrong = wrong; |
||
| 98 | } |
||
| 99 | for (var g in grammar){
|
||
|
0 ignored issues
–
show
A for in loop automatically includes the property of any prototype object, consider checking the key using
hasOwnProperty.
When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically: var someObject;
for (var key in someObject) {
if ( ! someObject.hasOwnProperty(key)) {
continue; // Skip keys from the prototype.
}
doSomethingWith(key);
}
Loading history...
|
|||
| 100 | ASKME.sentences.push(g); |
||
| 101 | ASKME.tags.push(grammar[g]); |
||
| 102 | } |
||
| 103 | |||
| 104 | // Send request |
||
| 105 | remote(ASKME); |
||
| 106 | |||
| 107 | // Backup |
||
| 108 | ASKME.rule = grammar |
||
| 109 | ASKME.callback = callback; |
||
| 110 | ASKME.token = setTimeout(function(){
|
||
| 111 | ASKME = false; |
||
| 112 | if (timeout <= 0){
|
||
| 113 | callback(false, end); |
||
| 114 | } else {
|
||
| 115 | SARAH.askme(tts, grammar, 0, callback); |
||
| 116 | } |
||
| 117 | }, timeout || 16000); |
||
|
0 ignored issues
–
show
|
|||
| 118 | } |
||
| 119 | |||
| 120 | var answerme = function(req, res, next){ res.end();
|
||
|
0 ignored issues
–
show
|
|||
| 121 | if (!ASKME){ return; }
|
||
| 122 | if (ASKME.token){ clearTimeout(ASKME.token); }
|
||
| 123 | ASKME.callback(req.param('dictation') || req.param('tag'), end);
|
||
| 124 | } |
||
| 125 | |||
| 126 | // ------------------------------------------ |
||
| 127 | // ROUTER |
||
| 128 | // ------------------------------------------ |
||
| 129 | |||
| 130 | var Router = express.Router(); |
||
| 131 | |||
| 132 | Router.all('/sarah/:name', function(req, res, next) {
|
||
|
0 ignored issues
–
show
|
|||
| 133 | |||
| 134 | var name = req.params.name; |
||
| 135 | var options = {};
|
||
| 136 | extend(true, options, req.query); |
||
| 137 | extend(true, options, req.body); |
||
| 138 | |||
| 139 | // 1. Log action into rule engine |
||
| 140 | var entry = SARAH.RuleEngine.log(name, options); |
||
| 141 | // info('Rule Engine Log:', entry);
|
||
| 142 | |||
| 143 | // 3. Send back TTS |
||
| 144 | var callback = function(data){
|
||
| 145 | |||
| 146 | // Redirect to portlet (no rules, asknect, ...) |
||
| 147 | if (req.query.ajax){
|
||
| 148 | res.redirect('/plugin/'+name);
|
||
| 149 | return; |
||
| 150 | } |
||
| 151 | |||
| 152 | //res.writeHead(200, {'Content-Type': 'text/html; charset=utf-8'});
|
||
| 153 | if (!res.headersSent){
|
||
| 154 | res.set({'Content-Type': 'text/html; charset=utf-8'});
|
||
| 155 | } |
||
| 156 | |||
| 157 | // Speak |
||
| 158 | if (data && data.tts){
|
||
| 159 | var tts = SARAH.ScriptManager.speak(data.tts); |
||
| 160 | if (tts){ res.write(tts); }
|
||
| 161 | } |
||
| 162 | |||
| 163 | res.status(200).end(); |
||
| 164 | |||
| 165 | // Ask next from data |
||
| 166 | if (data && data.asknext){
|
||
| 167 | if (typeof data.asknext === 'string'){
|
||
| 168 | SARAH.asknext(data.asknext); |
||
| 169 | } else {
|
||
| 170 | SARAH.asknext(data.asknext.rule, data.asknext); |
||
| 171 | } |
||
| 172 | } |
||
| 173 | else if (options.asknext){
|
||
| 174 | SARAH.asknext(options.asknext); |
||
| 175 | } |
||
| 176 | |||
| 177 | // 4. Guess next action from RuleEngine |
||
| 178 | var next = SARAH.RuleEngine.next(entry); |
||
|
0 ignored issues
–
show
|
|||
| 179 | //info('Rule Engine Guess:', next);
|
||
| 180 | } |
||
| 181 | |||
| 182 | // 2. Run plugin's script |
||
| 183 | SARAH.run(name, options, callback, true); |
||
| 184 | }); |
||
| 185 | |||
| 186 | Router.all('/standby', function(req, res, next) {
|
||
|
0 ignored issues
–
show
|
|||
| 187 | var motion = req.query.motion == "True" ? true : false; |
||
| 188 | SARAH.ScriptManager.standBy(motion, req.query.client); |
||
| 189 | res.end(); |
||
| 190 | }); |
||
| 191 | |||
| 192 | Router.all('/askme', answerme);
|
||
| 193 | |||
| 194 | |||
| 195 | // ------------------------------------------ |
||
| 196 | // REMOTE |
||
| 197 | // ------------------------------------------ |
||
| 198 | |||
| 199 | var _callback = function (err, response, body){
|
||
| 200 | if (err || response.statusCode != 200) {
|
||
| 201 | warn("HTTP Error: ", err, response, body);
|
||
| 202 | return; |
||
|
0 ignored issues
–
show
|
|||
| 203 | } |
||
| 204 | }; |
||
| 205 | |||
| 206 | var remote = function(query, callback){
|
||
| 207 | var url = Config.http.remote; |
||
|
0 ignored issues
–
show
The variable
Config seems to be never declared. If this is a global, consider adding a /** global: Config */ comment.
This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed. To learn more about declaring variables in Javascript, see the MDN. Loading history...
|
|||
| 208 | var querystring = require('querystring');
|
||
| 209 | url += '?' + querystring.stringify(query); |
||
| 210 | |||
| 211 | info('Remote: ', url);
|
||
| 212 | var request = require('request');
|
||
| 213 | request({ 'url' : url }, callback || _callback);
|
||
| 214 | }; |
||
| 215 | |||
| 216 | var speak = function(tts, callback){
|
||
| 217 | |||
| 218 | tts = SARAH.ScriptManager.speak(tts, callback); |
||
| 219 | if (!tts){ return; }
|
||
| 220 | |||
| 221 | // Hook for TTS |
||
| 222 | var qs = {
|
||
| 223 | 'tts' : tts, |
||
| 224 | 'sync' : callback ? true : false |
||
| 225 | }; |
||
| 226 | return remote(qs, callback); |
||
| 227 | } |
||
| 228 | |||
| 229 | var answer = function(tts, callback){
|
||
| 230 | var answers = Config.bot.answers.split('|');
|
||
|
0 ignored issues
–
show
The variable
Config seems to be never declared. If this is a global, consider adding a /** global: Config */ comment.
This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed. To learn more about declaring variables in Javascript, see the MDN. Loading history...
|
|||
| 231 | var answer = answers[ Math.floor(Math.random() * answers.length)]; |
||
| 232 | return speak(answer, callback); |
||
| 233 | } |
||
| 234 | |||
| 235 | var shutup = function(once){
|
||
| 236 | var qs = { 'notts' : 'true' }
|
||
| 237 | if (once){ qs.once = true; }
|
||
| 238 | return remote(qs); |
||
| 239 | } |
||
| 240 | |||
| 241 | var play = function(path, callback){
|
||
| 242 | // Hook for TTS |
||
| 243 | var qs = {
|
||
| 244 | 'play' : path, |
||
| 245 | 'sync' : callback ? true : false |
||
| 246 | }; |
||
| 247 | return remote(qs, callback); |
||
| 248 | } |
||
| 249 | |||
| 250 | var stop = function(path){
|
||
| 251 | return remote({ 'stop' : path });
|
||
| 252 | } |
||
| 253 | |||
| 254 | var runApp = function(path, params){
|
||
| 255 | var qs = { 'run' : path };
|
||
| 256 | if (params){ qs.runp = params; }
|
||
| 257 | return remote(qs); |
||
| 258 | } |
||
| 259 | |||
| 260 | var activate = function(process){
|
||
| 261 | return remote({ 'activate' : process });
|
||
| 262 | } |
||
| 263 | |||
| 264 | var keyText = function(text){
|
||
| 265 | return remote({ 'keyText' : text });
|
||
| 266 | } |
||
| 267 | |||
| 268 | var keyUp = function(key, mod){
|
||
| 269 | var qs = { 'keyUp' : key };
|
||
| 270 | if (mod){ qs.keyMod = mod; }
|
||
| 271 | return remote(qs); |
||
| 272 | } |
||
| 273 | |||
| 274 | var keyDown = function(key, mod){
|
||
| 275 | var qs = { 'keyDown' : key };
|
||
| 276 | if (mod){ qs.keyMod = mod; }
|
||
| 277 | return remote(qs); |
||
| 278 | } |
||
| 279 | |||
| 280 | var keyPress = function(key, mod){
|
||
| 281 | var qs = { 'keyPress' : key };
|
||
| 282 | if (mod){ qs.keyMod = mod; }
|
||
| 283 | return remote(qs); |
||
| 284 | } |
||
| 285 | |||
| 286 | var face = function(pause){
|
||
| 287 | return remote({ 'face' : pause });
|
||
| 288 | } |
||
| 289 | |||
| 290 | var gesture = function(pause){
|
||
| 291 | return remote({ 'gesture' : pause });
|
||
| 292 | } |
||
| 293 | |||
| 294 | var listen = function(pause){
|
||
| 295 | return remote({ 'listen' : pause });
|
||
| 296 | } |
||
| 297 | |||
| 298 | var picture = function(device, path, type){
|
||
| 299 | var qs = { 'picture' : type || 'true' };
|
||
| 300 | |||
| 301 | if (path){ qs.picture = path; }
|
||
| 302 | if (device){ qs.device = device; }
|
||
| 303 | return remote(qs); |
||
| 304 | } |
||
| 305 | |||
| 306 | var recognize = function(path){
|
||
| 307 | return remote({ 'recognize' : path });
|
||
| 308 | } |
||
| 309 | |||
| 310 | var context = function(rules){
|
||
| 311 | return remote({ 'context' : rules });
|
||
| 312 | } |
||
| 313 | |||
| 314 | var grammar = function(rule, xml){
|
||
| 315 | return remote({ 'grammar' : rule , 'xml' : xml});
|
||
| 316 | } |
||
| 317 | |||
| 318 | var asknext = function(rule, options){
|
||
| 319 | var qs = {'asknext' : rule };
|
||
| 320 | if (options){ extend(true, qs, options); }
|
||
| 321 | return remote(qs); |
||
| 322 | } |
||
| 323 | |||
| 324 | // ------------------------------------------ |
||
| 325 | // PUBLIC |
||
| 326 | // ------------------------------------------ |
||
| 327 | |||
| 328 | var SARAH = {
|
||
| 329 | 'init' : init, |
||
| 330 | 'remote' : remote, |
||
| 331 | |||
| 332 | 'speak' : speak, |
||
| 333 | 'answer' : answer, |
||
| 334 | 'shutup' : shutup, |
||
| 335 | 'play' : play, |
||
| 336 | 'stop' : stop, |
||
| 337 | |||
| 338 | 'runApp' : runApp, |
||
| 339 | 'activate' : activate, |
||
| 340 | 'keyText' : keyText, |
||
| 341 | 'keyUp' : keyUp, |
||
| 342 | 'keyDown' : keyDown, |
||
| 343 | 'keyPress' : keyPress, |
||
| 344 | |||
| 345 | 'face' : face, |
||
| 346 | 'gesture' : gesture, |
||
| 347 | 'listen' : listen, |
||
| 348 | 'picture' : picture, |
||
| 349 | 'recognize' : recognize, |
||
| 350 | |||
| 351 | 'context' : context, |
||
| 352 | 'grammar' : grammar, |
||
| 353 | 'askme' : askme, |
||
| 354 | 'asknext' : asknext, |
||
| 355 | |||
| 356 | 'getRSSFeed' : getRSSFeed, |
||
| 357 | 'Router' : Router, |
||
| 358 | 'USERAGENT' : USERAGENT |
||
| 359 | } |
||
| 360 | |||
| 361 | // Exports SARAH singleton |
||
| 362 | exports.init = init; |