c45091e747c0d2b088cf32797662565ba49dbb63
[motion.git] / server / config / agi.js
1 'use strict';
2
3 var _ = require('lodash');
4
5 var agi = require('agi');
6 var util = require('util');
7 var moment = require('moment');
8 var xml = require('xml2js');
9 var wait = require('wait.for');
10 var sr = require('simple-random');
11 var path = require('path');
12 var config = require('../config/environment');
13 var sh = require('shelljs');
14
15 var db = require("odbc")();
16
17 var SquareProject = require('../models').SquareProject;
18 var Variable = require('../models').Variable;
19 var SquareOdbc = require('../models').SquareOdbc;
20 var Settings = require('../models').Settings;
21 var User = require('../models').User;
22 var VoiceQueue = require('../models').VoiceQueue;
23 var Trunk = require('../models').Trunk;
24 var Interval = require('../models').Interval;
25 var Upload = require('../models').Upload;
26 var ReportSquare = require('../models').ReportSquare;
27 var ReportSquareDetail = require('../models').ReportSquareDetail;
28 // var TempTable = require('../models').TempTable;
29 // var FakeTable = require('../models').FakeTable;
30 var users, trunks, variables, intervals, projects, sounds, queues, dbConnections, generalUniqueId;
31 var agiPort;
32 var weekDaysCollection = {
33   mon: 1,
34   tue: 2,
35   wed: 3,
36   thu: 4,
37   fri: 5,
38   sat: 6,
39   sun: 7
40 };
41 var monthsCollection = {
42   jan: 1,
43   feb: 2,
44   mar: 3,
45   apr: 4,
46   may: 5,
47   jun: 6,
48   jul: 7,
49   aug: 8,
50   sep: 9,
51   oct: 10,
52   nov: 11,
53   dec: 12
54 };
55 var methods = {};
56
57 methods.answer = function(context, vertex, callback) {
58   console.log('--ANSWER BLOCK--');
59   console.log('Answering the call...');
60   context.send('ANSWER\n', function(err, res) {
61     callback(err, res);
62   });
63 };
64
65 methods.custom_app = function(context, vertex, callback) {
66   console.log('--CUSTOM APP BLOCK--');
67   console.log('Executing custom app "' + vertex.application + '"...');
68   context.exec(vertex.application, vertex.options, function(err, res) {
69     if (callback) {
70       callback(err, res);
71     }
72   });
73 };
74
75
76 methods.dial = function(context, vertex, callback) {
77   console.log('--INTERNAL_DIAL BLOCK--');
78   if (!users) {
79     getUsers(false);
80   }
81   var sip = _.find(users, {
82     id: parseInt(vertex.sip_id)
83   });
84   console.log('Calling ' + util.format('SIP/%s', sip.name) + '...');
85   var parameters = [util.format('SIP/%s', sip.name), vertex.timeout, vertex.opts, vertex.url];
86   context.exec('DIAL', parameters.join(','),
87     function(err, res) {
88       callback(err, res);
89     });
90 };
91
92 methods.ext_dial = function(context, vertex, callback) {
93   console.log('--EXTERNAL_DIAL BLOCK--');
94   if (!trunks) {
95     getTrunks(false);
96   }
97   var trunk = _.find(trunks, {
98     id: parseInt(vertex.trunk_id)
99   });
100   console.log('Calling ' + util.format('SIP/%s', trunk.name) + '...');
101   var parameters = [util.format('SIP/%s@%s', vertex.phone, trunk.name), vertex.timeout, vertex.opts, vertex.url];
102   context.exec('DIAL', parameters.join(','),
103     function(err, res) {
104       callback(err, res);
105     });
106 };
107
108 methods.queue = function(context, vertex, callback) {
109   console.log('--QUEUE BLOCK--');
110   if (!queues) {
111     getQueues(false);
112   }
113   var queue = _.find(queues, {
114     name: vertex.queue_id
115   });
116   var announceOverride = getFilePath(vertex.file_id);
117   var parameters = [queue.name, vertex.opts, vertex.url, announceOverride, vertex.timeout, vertex.agi, vertex.macro, vertex.gosub, '', vertex.position];
118   console.log('Joining ' + queue.name + ' queue...');
119   context.exec('QUEUE', parameters.join(','), function(err, res) {
120     callback(err, res);
121   });
122 };
123
124 methods.voicemail = function(context, vertex, callback) {
125   console.log('--VOICEMAIL BLOCK--');
126   var parameters = [util.format('%s@%s', vertex.boxnumber, vertex.context), vertex.opts];
127   console.log('Starting voicemail recording...');
128   context.exec('VOICEMAIL', parameters.join(','), function(err, res) {
129     callback(err, res);
130   });
131 };
132
133 // methods.callback = function(context, vertex, callback) {
134 //   context.send('ANSWER\n', function(err, res) {
135 //     callback(err, res);
136 //   });
137 // };
138
139 methods.math = function(context, vertex, callback) {
140   console.log('--MATH BLOCK--');
141   console.log('Calculating expression...');
142   var result = eval(vertex.operation);
143   console.log('Saving result in variable...');
144   setVariable(context, vertex.variable_id, result, callback);
145 };
146
147 // methods.background = function(context, vertex, callback) {
148 //   var parameters = [getFilePath(vertex.file_id), vertex.opts];
149 //   context.exec('BACKGROUND', parameters.join('|'), function(err, res) {
150 //     callback(err, res);
151 //   });
152 // };
153
154 methods.playback = function(context, vertex, callback) {
155   console.log('--PLAYBACK BLOCK--');
156   var audiofile = getFilePath(vertex.file_id);
157   // var audiofile = '/var/www/html/files/sounds/d0269ff87187df665ece75538e4cddfd';
158   var parameters = [audiofile, vertex.opts];
159   console.log('Executing playback...');
160   context.exec('PLAYBACK', parameters.join(','), function(err, res) {
161     callback(err, res);
162   });
163 };
164
165 methods.menu = function(context, vertex, callback) {
166   console.log('--MENU BLOCK--');
167   var res = {};
168   console.log('There are ' + vertex.retry + ' retries');
169   if (vertex.retry > 0) {
170     vertex.retry--;
171     // var announce = getFilePath(vertex.file_id);
172     var announce = '/var/www/html/files/sounds/b19642d2f71e9cfffbd783fcc79c7415';
173     // console.log(announce);
174     console.log('Announcing and waiting for user entry...');
175     context.send(util.format('GET DATA %s %s %s\n', announce, parseInt(vertex.response) * 1000, vertex.digit),
176       function(err, res) {
177         var squareDetail = {
178           uniqueid: context.uniqueid,
179           node: vertex.label,
180           application: vertex.tag,
181           data: res.result || null
182         }
183         ReportSquareDetail
184           .create(squareDetail)
185           .catch(function(err) {
186             console.log('Error saving ' + vertex.tag + ':' + vertex.label + ' block log', err);
187           });
188         if (res.result !== ' (timeout)') {
189           var variableId = parseInt(vertex.variable_id);
190           if (variableId) {
191             console.log('Saving user entry in a variable...');
192             setVariable(context, variableId, res.result);
193           }
194           res.data = res.result;
195           callback(null, res);
196         } else {
197           console.log('Menu timeout!');
198           res = {
199             code: 200,
200             result: '0',
201             data: 't'
202           };
203           callback(null, res);
204         }
205       });
206
207   } else {
208     console.log('Run out of retries!');
209     res = {
210       code: 200,
211       result: '0',
212       data: '-'
213     };
214     callback(null, res);
215   }
216 };
217
218 methods.saynumber = function(context, vertex, callback) {
219   console.log('--SAYNUMBER BLOCK--');
220   console.log('Saying number...');
221   context.send(util.format('SAY NUMBER %s "%s"\n', vertex.number, vertex.escape_digits),
222     function(err, res) {
223       callback(err, res);
224     });
225 };
226
227 methods.sayphonetic = function(context, vertex, callback) {
228   console.log('--SAYPHONETIC BLOCK--');
229   console.log('Saying phonetic...');
230   context.send(util.format('SAY PHONETIC %s "%s"\n', vertex.text, vertex.escape_digits),
231     function(err, res) {
232       callback(err, res);
233     });
234 };
235
236 methods.tts = function(context, vertex, callback) {
237   console.log('--GOOGLE_TTS BLOCK--');
238   var parameters = [path.join(config.root, 'server/config/agi_scripts', 'googletts.agi'), encodeURIComponent(vertex.text), vertex.google_tts_language];
239   // var parameters = ['/var/www/html/agisquare/agiscripts/googletts.agi', encodeURIComponent(vertex.text), vertex.google_tts_language];
240   console.log('Calling Google TTS API...');
241   context.exec('AGI', parameters.join(','),
242     function(err, res) {
243       callback(err, res);
244     });
245 };
246
247 methods.ispeechtts = function(context, vertex, callback) {
248   console.log('--ISPEECH_TTS BLOCK--');
249   // var parameters = [path.join(config.root, 'server/config/agi_scripts', 'ispeech-tts.agi'), encodeURIComponent(vertex.text), vertex.ispeech_tts_language, '', '', vertex.key];
250   var parameters = ['/var/www/html/agisquare/agiscripts/ispeech-tts.agi', encodeURIComponent(vertex.text), vertex.ispeech_tts_language, '', '', vertex.key];
251   console.log('Calling iSpeech TTS API...');
252   context.exec('AGI', parameters.join(','),
253     function(err, res) {
254       callback(err, res);
255     });
256 };
257
258 methods.getdigits = function(context, vertex, callback) {
259   console.log('--GETDIGITS BLOCK--');
260   var res = {};
261   console.log('There are ' + vertex.retry + ' retries');
262   if (vertex.retry > 0) {
263     vertex.retry--;
264     var announce = getFilePath(vertex.file_id);
265     // var announce = '/var/www/html/files/sounds/b19642d2f71e9cfffbd783fcc79c7415';
266     console.log('Announcing and waiting for user entry...');
267     context.send(util.format('GET DATA %s %s %s\n', announce, parseInt(vertex.response) * 1000, vertex.maxdigit),
268       function(err, res) {
269         var squareDetail = {
270           uniqueid: context.uniqueid,
271           node: vertex.label,
272           application: vertex.tag,
273           data: res.result || null
274         }
275         ReportSquareDetail
276           .create(squareDetail)
277           .catch(function(err) {
278             console.log('Error saving ' + vertex.tag + ':' + vertex.label + ' block log', err);
279           });
280         if (res.result && res.result.length >= parseInt(vertex.mindigit)) {
281           if (res.result !== '-1') {
282             console.log('There is a result, is over the minimum length and not due to an hangup!');
283             var variableId = parseInt(vertex.variable_id);
284             if (variableId) {
285               console.log('Saving user entry in a variable...');
286               setVariable(context, variableId, res.result);
287             }
288             res = {
289               code: 200,
290               result: '0',
291               data: 'x'
292             };
293             callback(null, res);
294           } else {
295             console.log('Channel hangup!');
296             res = {
297               code: 500,
298               result: '-1'
299             };
300             callback(null, res);
301           }
302         } else {
303           console.log('No entry or not long enough!');
304           res = {
305             code: 200,
306             result: '0',
307             data: 'i'
308           };
309           callback(null, res);
310         }
311       });
312   } else {
313     console.log('Run out of retries!');
314     res = {
315       code: 500,
316       result: '-1'
317     };
318     callback(null, res);
319   }
320 };
321
322 methods.record = function(context, vertex, callback) {
323   console.log('--RECORD BLOCK--');
324   var saveName = sr();
325   // FakeTable
326   //   .create({
327   //     name: vertex.name,
328   //     filename: saveName
329   //   })
330   //   .then(function() {
331   console.log('Starting call recording...');
332   context.send(util.format('RECORD FILE %s wav "%s" %s %s %s %s\n', path.join(config.root, 'server/files/recordings', saveName), vertex.escape_digits, vertex.timeout, null, true, null), function(
333     err, res) {
334     if (res.code === 200 && res.result !== '-1') {
335       console.log('Saving recording filename in RECORDING_SAVENAME variable...');
336       context.send(util.format('SET VARIABLE %s %s\n', 'RECORDING_SAVENAME', saveName), function(err, res) {
337         callback(err, res);
338       });
339     } else {
340       callback(err, res);
341     }
342   });
343   // })
344   // .catch(function(err) {
345   //   console.log(err);
346   // });
347 };
348
349 methods.gotoiftime = function(context, vertex, callback) { //single or multiple intervals
350   console.log('--GOTOIFTIME BLOCK--');
351   var res;
352   var valid = 'false';
353   var gotoIntervals;
354   if (!intervals) {
355     getIntervals(false);
356   }
357   var interval = _.find(intervals, {
358     id: parseInt(vertex.interval_id)
359   });
360   if (!interval.IntervalId) {
361     gotoIntervals = _.filter(intervals, {
362       IntervalId: parseInt(interval.id)
363     });
364   }
365   if (gotoIntervals) {
366     console.log('Is a group of intervals!');
367     if (gotoIntervals.length) {
368       console.log('There are ' + gotoIntervals.length + ' intervals!');
369       var subInterval;
370       console.log('Checking if at least one interval is valid...');
371       gotoIntervals.forEach(function(elem, index) {
372         subInterval = splitInterval(elem.interval);
373         if (isIntervalValid(subInterval)) {
374           console.log('Interval ' + index + ' is valid!');
375           valid = 'true';
376         }
377       });
378     } else {
379       console.log('There are no sub intervals, so it will be always true!');
380       valid = 'true';
381     }
382   } else {
383     console.log('Is a single interval!');
384     var splittedInterval = splitInterval(interval.interval);
385     if (isIntervalValid(splittedInterval)) {
386       console.log('The interval is valid!');
387       valid = 'true';
388     }
389   }
390   console.log('The final result for the intervals is "' + valid + '"!');
391   res = {
392     code: 200,
393     result: '0',
394     data: valid
395   };
396   callback(null, res);
397 };
398
399 methods.vswitch = function(context, vertex, callback) {
400   console.log('--VARIABLE_SWITCH BLOCK--');
401   console.log('Getting the variable value and searching the right exit...');
402   var res = {
403     data: getVariable(context, vertex.variable_id)
404   };
405   callback(null, res);
406 };
407
408 // methods.goal = function(context, vertex, callback) {
409 // TempTable
410 //   .create({
411 //     status: 'GOAL',
412 //     goalname: vertex.goalname
413 //   })
414 //   .then(function(res) {
415 //     callback(null, res);
416 //   })
417 //   .catch(function(err) {
418 //     callback(err, null);
419 //   });
420 // };
421
422 methods.system = function(context, vertex, callback) {
423   console.log('--SYSTEM BLOCK--');
424   var squareDetail = {
425     uniqueid: context.uniqueid,
426     node: vertex.label,
427     application: vertex.tag,
428     data: _.trim(vertex.command) || null
429   }
430   ReportSquareDetail
431     .create(squareDetail)
432     .catch(function(err) {
433       console.log('Error saving ' + vertex.tag + ':' + vertex.label + ' block log', err);
434     });
435   console.log('Executing the system command...');
436   sh.exec(_.trim(vertex.command), function(code, output) {
437     var formattedOutput = output.replace(/(\r\n|\n|\r)/gm, "");
438     console.log('Program output:', formattedOutput);
439     console.log('Saving the output in a variable...');
440     setVariable(context, vertex.variable_id, '"' + formattedOutput + '"', callback);
441   });
442 };
443
444 methods.agi = function(context, vertex, callback) {
445   console.log('--AGI BLOCK--');
446   console.log('Executing the AGI command...');
447   context.exec('AGI', vertex.command, vertex.args,
448     function(err, res) {
449       callback(err, res);
450     });
451 };
452
453 methods.subproject = function(context, vertex, callback) {
454   console.log('--SUBPROJECT BLOCK--');
455   if (!projects) {
456     getProjects(false);
457   }
458   var project = _.find(projects, {
459     id: parseInt(vertex.project_id)
460   });
461   console.log('Moving to project ""' + project.name + '"...');
462   context.exec('AGI', util.format('agi://192.168.2.147/square,%s', project.name),
463     function(err, res) {
464       callback(err, res);
465     });
466 };
467
468 methods.ispeechasr = function(context, vertex, callback) {
469   console.log('--ISPEECH_ASR BLOCK--');
470   // var parameters = ['/var/www/html/agisquare/agiscripts/ispeech-asr.agi', vertex.ispeech_asr_language, '', (vertex.model === '0' ? '' : vertex.model), '1', '#', '', vertex.key];
471   var parameters = [path.join(config.root, 'server/config/agi_scripts', 'ispeech-asr.agi'), vertex.ispeech_asr_language, '', (vertex.model === '0' ? '' : vertex.model), '1', '#', '', vertex.key];
472   console.log('Calling Google ASR API...');
473   context.exec('AGI', parameters.join(','),
474     function(err, res) {
475       callback(err, res);
476     });
477 };
478
479 methods.database = function(context, vertex, callback) {
480   console.log('--DATABASE BLOCK--');
481   if (!dbConnections) {
482     getDbConnections(false);
483   }
484   // console.log(vertex);
485   var connection = _.find(dbConnections, {
486     id: parseInt(vertex.odbc_id)
487   });
488   console.log('Trying to get ODBC connection...');
489   var squareDetail = {
490     uniqueid: context.uniqueid,
491     node: vertex.label,
492     application: vertex.tag,
493     data: vertex.query || null
494   }
495   ReportSquareDetail
496     .create(squareDetail)
497     .catch(function(err) {
498       console.log('Error saving ' + vertex.tag + ':' + vertex.label + ' block log', err);
499     });
500   db.open(connection.dsn, function(err) {
501     if (err) {
502       console.log('Connection opening error: ', err);
503       var res = {
504         code: 500,
505         result: '-1'
506       };
507       callback(null, res);
508     } else {
509       console.log('Connection succeded, executing query...');
510       db.query(vertex.query, function(err, data) {
511         if (err) {
512           console.log('Query error: ', err);
513           var res = {
514             code: 500,
515             result: '-1'
516           };
517           callback(null, res);
518         } else {
519           console.log('Query executed, closing connection...');
520           db.close(function(err) {
521             if (err) {
522               console.log('Connection closing error: ', err);
523               var res = {
524                 code: 500,
525                 result: '-1'
526               };
527               callback(null, res);
528             } else {
529               console.log('Connection closed, saving resulting rows in a variable...');
530               setVariable(context, vertex.variable_id, data, callback);
531             }
532           });
533         }
534       });
535     }
536   });
537 };
538
539 methods.gotoc = function(context, vertex, callback) {
540   console.log('--GOTO BLOCK--');
541   console.log('Going to extension ' + vertex.extension + ' ...');
542   context.exec('DIAL', util.format('Local/%s@%s', vertex.extension, vertex.context),
543     function(err, res) {
544       callback(err, res);
545     });
546 };
547
548 methods.gotoif = function(context, vertex, callback) {
549   console.log('--GOTOIF BLOCK--');
550   var res = {
551     code: 200,
552     result: '0'
553   };
554   console.log('Evaluating condition...');
555   res.data = String(eval(vertex.condition) ? 'true' : 'false');
556   console.log('The result is ' + res.data);
557   callback(null, res);
558 };
559
560 methods.hangup = function(context, vertex, callback) {
561   console.log('--HANGUP BLOCK--');
562   console.log('Hangin up the call...');
563   context.hangup();
564 };
565
566 methods.noop = function(context, vertex, callback) {
567   console.log('--NOOP BLOCK--');
568   var output = (vertex.output) ? '\"' + vertex.output + '\"' : '\"' +
569     vertex.label + '\"';
570   console.log('NOOP value is ' + output);
571   context.exec('NOOP', output,
572     function(err, res) {
573       callback(err, res);
574     });
575 };
576
577 methods.saydigits = function(context, vertex, callback) {
578   console.log('--SAYDIGITS BLOCK--');
579   console.log('Saying digits...');
580   context.send(util.format('SAY DIGITS %s "%s"\n', vertex.digits, vertex.escape_digits),
581     function(err, res) {
582       callback(err, res);
583     });
584 };
585
586 methods.set = function(context, vertex, callback) {
587   console.log('--SET BLOCK--');
588   console.log('Saving value in a variable...');
589   setVariable(context, vertex.variable_id, vertex.variable_value, callback);
590 };
591
592 function getVertices(root) {
593   return _.reduce(root, function(result, edge, tag) {
594
595     if (tag === 'mxcell') {
596       return result;
597     }
598
599     if (_.isArray(edge)) {
600       _.forIn(edge, function(value, key) {
601         result[value.$.id] = value.$;
602         result[value.$.id].tag = tag;
603       });
604       return result;
605     }
606
607     result[edge.$.id] = edge.$;
608     result[edge.$.id].tag = tag;
609     return result;
610
611   }, {});
612 }
613
614 function getTargetBySource(root, source, value) {
615   return _.result(_.find(root.mxcell, function(edge) {
616     if (edge.$.edge && edge.$.source) {
617       if (edge.$.source === source) {
618         if (edge.$.value && edge.$.value !== value) {
619           return false;
620         }
621         return true;
622       }
623     }
624     return false;
625   }), '$.target');
626 }
627
628 function replaceAllVariables(context, vertex, callback) {
629   var obj = vertex;
630   for (var key in obj) {
631     var res = obj[key].match(/{+(.*?)}/gi);
632     if (res) {
633       // console.log(res);
634       for (var variable in res) {
635         var value = wait.forMethod(context, 'getVariable',
636           res[variable].substring(1, res[variable].length - 1));
637         if (value.code === 200 && value.result !== '0') {
638           var string = value.result.substring(value.result.lastIndexOf("(") + 1,
639             value.result.lastIndexOf(")"))
640           obj[key] = obj[key].replace(res[variable], string);
641           // console.log('Replace', res[variable], string);
642         }
643       }
644     }
645   }
646   obj.replaced = true;
647   return obj;
648 }
649
650 function xstart(context) {
651   var root = context.root;
652   var vertices = context.vertices;
653
654   if (root.start) {
655     if (!_.isArray(root.start)) {
656       var source = root.start.$.id;
657       var res = {};
658       do {
659         var vertex = vertices[source].replaced ? vertices[source] : replaceAllVariables(context, vertices[source]);
660         if (vertex) {
661           if (_.isFunction(methods[vertex.tag])) {
662             res = wait.for(methods[vertex.tag], context, vertex);
663           } else {
664             res = wait.for(methods.noop, context, vertex);
665           }
666           if (res.code === 200 && res.result !== '-1') {
667             var target = getTargetBySource(root, source, res.data);
668             if (target) {} else {
669               if (vertex.tag === 'menu') {
670                 if (vertex.retry > 0) {
671                   target = getTargetBySource(root, source, 'i');
672                   if (target) {} else {
673                     target = source;
674                   }
675                 }
676               }
677               if (vertex.tag === 'getdigits' && vertex.retry > 0) {
678                 target = source;
679               }
680             }
681             source = target;
682           } else {
683             source = null;
684           }
685         } else {
686           source = null;
687         }
688       }
689       while (source);
690       console.log('No target found, hangup!');
691       context.hangup();
692     }
693   }
694 }
695
696 function xfinally(context) {
697   var root = context.root;
698   var vertices = context.vertices;
699
700   if (root.finally) {
701     if (!_.isArray(root.finally)) {
702       var source = root.finally.$.id;
703       var res = {};
704       do {
705         var vertex = replaceAllVariables(context, vertices[source]);
706         if (vertex) {
707           if (_.isFunction(methods[vertex.tag])) {
708             res = wait.for(methods[vertex.tag], context, vertex);
709           } else {
710             res = wait.for(methods.noop, context, vertex);
711           }
712           if (res.code === 200 && res.result !== '-1') {
713             var target = getTargetBySource(root, source, res.data);
714             if (target) {} else {
715               if (vertex.tag === 'menu') {
716                 if (vertex.retry > 0) {
717                   target = getTargetBySource(root, source, 'i');
718                   if (target) {} else {
719                     target = source;
720                   }
721                 }
722               }
723               if (vertex.tag === 'getdigits' && vertex.retry > 0) {
724                 target = source;
725               }
726             }
727             source = target;
728           } else {
729             source = null;
730           }
731         } else {
732           source = null;
733         }
734       } while (source);
735       context.hangup();
736       console.log('No target found, stopping "Finally" branch!');
737     }
738   }
739   context.end();
740 }
741
742
743 function main(context) {
744
745   context.on('error', function(err) {
746     console.log('//Error:', err);
747   });
748
749   context.on('close', function() {
750     console.log('//Context close');
751   });
752
753   context.on('hangup', function() {
754     if (context.finally) {
755       context.finally = false;
756       console.log('//Starting "Finally" branch!');
757       wait.launchFiber(xfinally, context);
758     } else {
759       ReportSquare.update({
760           leaveAt: moment().format("YYYY-MM-DD HH:mm:ss")
761         }, {
762           where: {
763             uniqueid: context.uniqueid
764           }
765         })
766         .catch(function(err) {
767           console.log('Error saving exit time from IVR', err);
768         });
769       console.log('//Context Hangup');
770     }
771   });
772
773   context.on('response', function(res) {
774     //console.log('Response:', res);
775   });
776
777   context.on('variables', function(vars) {
778     console.log(vars);
779     console.log('Received new call from: ' + vars.agi_callerid +
780       ' with uniqueid: ' + vars.agi_uniqueid);
781     var squareLog = {
782       network: vars.agi_network,
783       network_script: vars.agi_network_script,
784       request: vars.agi_request,
785       channel: vars.agi_channel,
786       language: vars.agi_language,
787       type: vars.agi_type,
788       uniqueid: vars.agi_uniqueid,
789       version: vars.agi_version,
790       callerid: vars.agi_callerid,
791       calleridname: vars.agi_calleridname,
792       callingpres: vars.agi_callingpres,
793       callingani2: vars.agi_callingani2,
794       callington: vars.agi_callington,
795       callingtns: vars.agi_callingtns,
796       dnid: vars.agi_dnid,
797       rdnis: vars.agi_rdnis,
798       context: vars.agi_context,
799       extension: vars.agi_extension,
800       priority: vars.agi_priority,
801       enhanced: vars.agi_enhanced,
802       accountcode: vars.agi_accountcode,
803       threadid: vars.agi_threadid,
804       project_name: vars.agi_arg_1,
805       joinAt: moment().format("YYYY-MM-DD HH:mm:ss")
806     };
807
808     ReportSquare
809       .create(squareLog)
810       .catch(function(err) {
811         console.log('Error saving enter time for IVR', err);
812       });
813
814     if (vars.agi_arg_1) {
815       SquareProject
816         .findOne({
817           where: {
818             name: vars.agi_arg_1
819           },
820           attributes: ['id', 'production']
821         })
822         .then(function(project) {
823           if (project) {
824             if (project.production) {
825               xml.parseString(project.production, {
826                 normalizeTags: true,
827                 explicitArray: false
828               }, function(err, result) {
829                 var root = result.mxgraphmodel.root;
830                 if (root) {
831                   context.root = root;
832                   context.finally = true;
833                   context.uniqueid = vars.agi_uniqueid;
834                   context.vertices = getVertices(root);
835                   wait.launchFiber(xstart, context);
836                 } else {
837                   console.log('No root in project!');
838                   context.end();
839                 }
840               });
841             } else {
842               console.log('No project published!');
843               context.end();
844             }
845           } else {
846             console.log('No project found!');
847             context.end();
848           }
849         })
850         .catch(function(err) {
851           console.log('Error:', err);
852           context.end();
853         });
854     } else {
855       console.log('No project argument!');
856       context.end();
857     }
858   });
859 }
860
861 function splitInterval(interval) {
862   var finalInterval = {};
863   var splittedInterval = interval.split(',');
864   var intValues;
865   splittedInterval.forEach(function(element, index) {
866     switch (index) {
867       case 0:
868         if (element !== '*') {
869           intValues = element.split('-');
870           finalInterval.t_from = moment(intValues[0], 'HH:mm');
871           finalInterval.t_to = moment(intValues[1], 'HH:mm');
872         } else {
873           finalInterval.t_from = null;
874           finalInterval.t_to = null;
875         }
876         break;
877       case 1:
878         if (element !== '*') {
879           intValues = element.split('-');
880           finalInterval.wd_from = weekDaysCollection[intValues[0]];
881           finalInterval.wd_to = intValues[1] ? weekDaysCollection[intValues[1]] : null;
882         } else {
883           finalInterval.wd_from = null;
884           finalInterval.wd_to = null;
885         }
886         break;
887       case 2:
888         if (element !== '*') {
889           intValues = element.split('-');
890           finalInterval.md_from = intValues[0];
891           finalInterval.md_to = intValues[1] ? intValues[1] : null;
892         } else {
893           finalInterval.md_from = null;
894           finalInterval.md_to = null;
895         }
896         break;
897       case 3:
898         if (element !== '*') {
899           intValues = element.split('-');
900           finalInterval.m_from = monthsCollection[intValues[0]];
901           finalInterval.m_to = intValues[1] ? monthsCollection[intValues[1]] : null;
902         } else {
903           finalInterval.m_from = null;
904           finalInterval.m_to = null;
905         }
906         break;
907       default:
908
909     }
910
911
912   });
913   return finalInterval;
914 }
915
916 function isIntervalValid(interval) {
917   var hour = moment().format("HH:mm");
918   var day = moment().format("E");
919   var monthDay = moment().format("D");
920   var month = moment().format("M");
921   var tValid, wdValid, mdValid, mValid;
922   var monthsNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
923   var weekDaysNumbers = [1, 2, 3, 4, 5, 6, 7];
924   var daysOfMonthsNumbers = [];
925   for (var i = 1; i <= 31; i++) {
926     daysOfMonthsNumbers.push(i);
927   }
928   tValid = (interval.t_from && interval.t_to) ? moment(hour, "HH:mm").isBetween(moment(interval.t_from, "HH:mm"), moment(interval.t_to, "HH:mm")) : true;
929   if (interval.wd_from) {
930     if (interval.wd_to) {
931       var validWeekdays = _.filter(weekDaysNumbers, function(elem) {
932         return (elem >= interval.wd_from) || (elem <= interval.wd_to);
933       });
934       wdValid = (validWeekdays.indexOf(parseInt(day)) !== -1) ? true : false;
935     } else {
936       wdValid = (parseInt(day) === interval.wd_from) ? true : false;
937     }
938   } else {
939     wdValid = true;
940   }
941   if (interval.md_from) {
942     if (interval.md_to) {
943       var validMonthsdays = _.filter(daysOfMonthsNumbers, function(elem) {
944         return (elem >= interval.md_from) || (elem <= interval.md_to);
945       });
946       mdValid = (validMonthsdays.indexOf(parseInt(monthDay)) !== -1) ? true : false;
947     } else {
948       mdValid = (parseInt(monthDay) === interval.md_from) ? true : false;
949     }
950   } else {
951     mdValid = true;
952   }
953   if (interval.m_from) {
954     if (interval.m_to) {
955       var validMonths = _.filter(monthsNumbers, function(elem) {
956         return (elem >= interval.m_from) || (elem <= interval.m_to);
957       });
958       mValid = (validMonths.indexOf(parseInt(month)) !== -1) ? true : false;
959     } else {
960       mValid = (parseInt(month) === interval.m_from) ? true : false;
961     }
962   } else {
963     mValid = true;
964   }
965
966   return tValid && wdValid && mdValid && mValid;
967
968 }
969
970 function setVariable(context, id, value, callback) {
971   console.log('Setting variable...');
972   if (!variables) {
973     getVariables(false);
974   }
975   var variable = _.find(variables, {
976     id: parseInt(id)
977   });
978   console.log('Sending set variable, value is ', value);
979   context.send(util.format('SET VARIABLE %s %s\n', variable.name, value), function(err, res) {
980     if (callback) {
981       callback(err, res);
982     }
983   });
984 }
985
986 function getVariable(context, id) {
987   if (!variables) {
988     getVariables(false);
989   }
990   var variable = _.find(variables, {
991     id: parseInt(id)
992   });
993   var value = wait.forMethod(context, 'getVariable', variable.name);
994   if (value.code === 200 && value.result !== '0') {
995     return value.result.substring(value.result.lastIndexOf("(") + 1,
996       value.result.lastIndexOf(")"));
997   }
998   return '';
999 }
1000
1001 function getFilePath(id) {
1002   if (!sounds) {
1003     getSounds(false);
1004   }
1005   if (id > 0) {
1006     var file = _.find(sounds, {
1007       id: parseInt(id)
1008     });
1009     return util.format('%s/%s', file.converted_path, file.save_name);
1010   }
1011   return '';
1012 }
1013
1014 function getUsers(synch) {
1015   User
1016     .findAll()
1017     .then(function(result) {
1018       users = _.clone(result);
1019       if (synch) {
1020         synchUpdates(User, users, 'id');
1021       }
1022     })
1023     .catch(function(err) {
1024       console.log(err);
1025     });
1026 }
1027
1028 function getQueues(synch) {
1029   VoiceQueue
1030     .findAll()
1031     .then(function(result) {
1032       queues = _.clone(result);
1033       if (synch) {
1034         synchUpdates(VoiceQueue, queues, 'name');
1035       }
1036     })
1037     .catch(function(err) {
1038       console.log(err);
1039     });
1040 }
1041
1042 function getTrunks(synch) {
1043   Trunk
1044     .findAll()
1045     .then(function(result) {
1046       trunks = _.clone(result);
1047       if (synch) {
1048         synchUpdates(Trunk, trunks, 'id');
1049       }
1050     })
1051     .catch(function(err) {
1052       console.log(err);
1053     });
1054 }
1055
1056 function getVariables(synch) {
1057   Variable
1058     .findAll()
1059     .then(function(result) {
1060       variables = _.clone(result);
1061       if (synch) {
1062         synchUpdates(Variable, variables, 'id');
1063       }
1064     })
1065     .catch(function(err) {
1066       console.log(err);
1067     });
1068 }
1069
1070 function getDbConnections(synch) {
1071   SquareOdbc
1072     .findAll()
1073     .then(function(result) {
1074       dbConnections = _.clone(result);
1075       if (synch) {
1076         synchUpdates(SquareOdbc, dbConnections, 'id');
1077       }
1078     })
1079     .catch(function(err) {
1080       console.log(err);
1081     });
1082 }
1083
1084 function getIntervals(synch) {
1085   Interval
1086     .findAll()
1087     .then(function(result) {
1088       intervals = _.clone(result);
1089       if (synch) {
1090         synchUpdates(Interval, intervals, 'id');
1091       }
1092     })
1093     .catch(function(err) {
1094       console.log(err);
1095     });
1096 }
1097
1098 function getProjects(synch) {
1099   SquareProject
1100     .findAll()
1101     .then(function(result) {
1102       projects = _.clone(result);
1103       if (synch) {
1104         synchUpdates(SquareProject, projects, 'id');
1105       }
1106     })
1107     .catch(function(err) {
1108       console.log(err);
1109     });
1110 }
1111
1112 function getSounds(synch) {
1113   Upload
1114     .findAll()
1115     .then(function(result) {
1116       sounds = _.clone(result);
1117       if (synch) {
1118         synchUpdates(Upload, sounds, 'id');
1119       }
1120     })
1121     .catch(function(err) {
1122       console.log(err);
1123     });
1124 }
1125
1126 function synchUpdates(Model, collection, key) {
1127   var condition = {};
1128   Model.afterCreate(function(doc) {
1129     condition[key] = doc[key];
1130     updateCollection(collection, condition, doc);
1131   });
1132   Model.afterUpdate(function(doc) {
1133     condition[key] = doc[key];
1134     updateCollection(collection, condition, doc);
1135   });
1136   Model.afterDestroy(function(doc) {
1137     condition[key] = doc[key];
1138     _.remove(collection, condition);
1139   });
1140 }
1141
1142 function updateCollection(collection, condition, doc) {
1143   var oldItem = _.find(collection, condition);
1144   var index = collection.indexOf(oldItem);
1145   if (oldItem) {
1146     _.merge(collection[index], doc);
1147   } else {
1148     collection.unshift(doc);
1149   }
1150 }
1151
1152 function getPort() {
1153   Settings
1154     .findOne()
1155     .then(function(result) {
1156       agiPort = result.agi_port;
1157     })
1158     .catch(function(err) {
1159       console.log(err);
1160     });
1161 }
1162
1163 module.exports = function() {
1164   console.log('Starting Cally Square AGI...');
1165   getPort();
1166   var server = agi.createServer(main).listen(agiPort ? agiPort : 4573);
1167   getUsers(true);
1168   getQueues(true);
1169   getTrunks(true);
1170   getVariables(true);
1171   getIntervals(true);
1172   getProjects(true);
1173   getSounds(true);
1174   getDbConnections(true);
1175 };