diff --git a/README.md b/README.md index 9f9a48a..eae5fce 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ Create AGI server with ding-dong. Use with Asterisk for fast telephony apps. [Fork of node-agi](http://github.com/brianc/node-agi) -## install +## Install ``` npm install ding-dong [--save] @@ -60,10 +60,13 @@ context.hangup(function(err, res) { ``` -## Projects +Projects +======== [Voicer](http://github.com/antirek/voicer) - AGI yandex voice recognizer for Asterisk +[agi-number-archer](http://github.com/antirek/agi-number-archer) - AGI server for find region code of phone number (Russia) + ## Links [Asterisk AGI](https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+AGI+Commands) diff --git a/lib/context.js b/lib/context.js index 27c474f..d58d9ad 100644 --- a/lib/context.js +++ b/lib/context.js @@ -2,14 +2,14 @@ var Readable = require('readable-stream'); var EventEmitter = require('events').EventEmitter; var state = require('./state'); -var Context = function(stream) { +var Context = function (stream) { EventEmitter.call(this); this.stream = new Readable(); this.stream.wrap(stream); this.state = state.init; this.msg = ""; var self = this; - this.stream.on('readable', function() { + this.stream.on('readable', function () { //always keep the 'leftover' part of the message self.msg = self.read(); }); @@ -36,7 +36,7 @@ Context.prototype.read = function() { return ""; }; -Context.prototype.readVariables = function(msg) { +Context.prototype.readVariables = function (msg) { var lines = msg.split('\n'); for(var i = 0; i < lines.length; i++) { var line = lines[i]; @@ -50,7 +50,7 @@ Context.prototype.readVariables = function(msg) { return ""; }; -Context.prototype.readResponse = function(msg) { +Context.prototype.readResponse = function (msg) { var lines = msg.split('\n'); for(var i = 0; i < lines.length; i++) { this.readResponseLine(lines[i]); @@ -58,9 +58,11 @@ Context.prototype.readResponse = function(msg) { return ""; }; -Context.prototype.readResponseLine = function(line) { +Context.prototype.readResponseLine = function (line) { if(!line) return; + var parsed = /^(\d{3})(?: result=)(.*)/.exec(line); + if(!parsed) { return this.emit('hangup'); } @@ -92,7 +94,7 @@ Context.prototype.exec = function() { var last = args.pop(); if(typeof last !== 'function') { args.push(last); - last = function() { } + last = function () { } } this.send('EXEC ' + args.join(' ') + '\n', last); }; @@ -101,16 +103,112 @@ Context.prototype.dial = function (num, timeout, params, cb) { this.exec('Dial', num + ',' + timeout + ',' + params, cb); }; +Context.prototype.databaseDel = function (family, key, cb) { + this.send('DATABASE DEL ' + family + ' ' + key + '\n', cb || function () { }); +}; + +Context.prototype.databaseDelTree = function (family, keytree, cb) { + this.send('DATABASE DELTREE ' + family + ' ' + keytree + '\n', cb || function () { }); +}; + +Context.prototype.databaseGet = function (family, key, cb) { + this.send('DATABASE GET ' + family + ' ' + key + '\n', cb || function () { }); +}; + +Context.prototype.databasePut = function (family, key, value, cb) { + this.send('DATABASE PUT ' + family + ' ' + key + ' ' + value + '\n', cb || function () { }); +}; + +Context.prototype.speechCreate = function (engine, cb) { + this.send('SPEECH CREATE ' + engine + '\n', cb || function () { }); +}; + +Context.prototype.speechDestroy = function (cb) { + this.send('SPEECH DESTROY\n', cb || function () { }); +}; + +Context.prototype.speechActivateGrammar = function (name, cb) { + this.send('SPEECH ACTIVATE GRAMMAR ' + name + '\n', cb || function () { }); +}; + +Context.prototype.speechDeactivateGrammar = function (name, cb) { + this.send('SPEECH DEACTIVATE GRAMMAR ' + name + '\n', cb || function () { }); +}; + +Context.prototype.speechLoadGrammar = function (name, path, cb) { + this.send('SPEECH LOAD GRAMMAR ' + name + ' ' + path + '\n', cb || function () { }); +}; + +Context.prototype.speechUnloadGrammar = function (name, cb) { + this.send('SPEECH UNLOAD GRAMMAR ' + name + '\n', cb || function () { }); +}; + +Context.prototype.speechSet = function (name, value, cb) { + this.send('SPEECH SET ' + name + ' ' + value + '\n', cb || function () { }); +}; + +Context.prototype.speechRecognize = function (prompt, timeout, offset, cb) { + this.send('SPEECH RECOGNIZE ' + prompt + ' ' + timeout + ' ' + offset + '\n', cb || function () { }); +}; + Context.prototype.getVariable = function (name, cb) { - this.send('GET VARIABLE ' + name + '\n', cb || function() { }); + this.send('GET VARIABLE ' + name + '\n', cb || function () { }); }; Context.prototype.getFullVariable = function (variable, channel, cb) { - this.send('GET FULL VARIABLE ' + variable + ' ' + channel + '\n', cb || function() { }); + this.send('GET FULL VARIABLE ' + variable + ' ' + channel + '\n', cb || function () { }); +}; + +Context.prototype.getData = function (file, timeout, maxdigits, cb) { + this.send('GET DATA ' + file + ' ' + timeout + ' ' + maxdigits + '\n', cb || function () { }); +}; + +Context.prototype.getOption = function (file, escape_digits, timeout, cb) { + this.send('GET OPTION ' + file + ' "' + escape_digits + '" ' + timeout + '\n', cb || function () { }); +}; + +Context.prototype.receiveChar = function (timeout, cb) { + this.send('RECEIVE CHAR ' + timeout + '\n', cb || function () { }); +}; + +Context.prototype.receiveText = function (timeout, cb) { + this.send('RECEIVE TEXT ' + timeout + '\n', cb || function () { }); +}; + +Context.prototype.setAutoHangup = function (seconds, cb) { + this.send('SET AUTOHANGUP ' + seconds + '\n', cb || function () { }); +}; + +Context.prototype.setCallerID = function (number, cb) { + this.send('SET CALLERID ' + number + '\n', cb || function () { }); +}; + +Context.prototype.setContext = function (context, cb) { + this.send('SET CONTEXT ' + context + '\n', cb || function () { }); +}; + +Context.prototype.setExtension = function (extension, cb) { + this.send('SET EXTENSION ' + extension + '\n', cb || function () { }); +}; + +Context.prototype.setPriority = function (priority, cb) { + this.send('SET PRIORITY ' + priority + '\n', cb || function () { }); +}; + +Context.prototype.setMusic = function (musicclass, cb) { + this.send('SET MUSIC ' + musicclass + '\n', cb || function () { }); }; Context.prototype.setVariable = function (name, value, cb) { - this.send('SET VARIABLE ' + name + ' ' + value + '\n', cb || function() { }); + this.send('SET VARIABLE ' + name + ' ' + value + '\n', cb || function () { }); +}; + +Context.prototype.sendImage = function (image, cb) { + this.send('SEND IMAGE ' + image + '\n', cb || function() { }); +}; + +Context.prototype.sendText = function (text, cb) { + this.send('SEND TEXT "' + text + '"\n', cb || function() { }); }; Context.prototype.channelStatus = function (name, cb) { @@ -118,12 +216,29 @@ Context.prototype.channelStatus = function (name, cb) { }; Context.prototype.answer = function (cb) { - this.send('ANSWER' + '\n', cb || function() { }); + this.send('ANSWER\n', cb || function () { }); +}; + +Context.prototype.verbose = function (message, level, cb) { + this.send('VERBOSE "' + message + '" ' + level + '\n', cb || function () { }); +}; + +Context.prototype.tddMode = function (value, cb) { + this.send('TDD MODE ' + value + '\n', cb || function () { }); +}; + +Context.prototype.noop = function (cb) { + this.send('NOOP\n', cb || function () { }); +}; + +Context.prototype.gosub = function (context, extension, priority, option, cb) { + var str = [context, extension, priority, option].join(' '); + this.send('GOSUB ' + str + '\n', cb || function () { }); }; Context.prototype.recordFile = function (filename, format, escape_digits, timeout, offset, beep, silence, cb) { var str = [ - '"'+filename+'"', + '"' + filename + '"', format, escape_digits, parseInt(timeout)*1000, @@ -134,19 +249,35 @@ Context.prototype.recordFile = function (filename, format, escape_digits, timeou this.send('RECORD FILE ' + str + '\n', cb || function() { }); }; -Context.prototype.sayNumber = function(number, escape_digits, cb) { +Context.prototype.sayNumber = function (number, escape_digits, cb) { this.send('SAY NUMBER ' + number + ' "' + escape_digits + '"' + '\n', cb || function() { }); }; -Context.prototype.sayDigits = function(digits, escape_digits, cb) { +Context.prototype.sayAlpha = function (number, escape_digits, cb) { + this.send('SAY ALPHA ' + number + ' "' + escape_digits + '"' + '\n', cb || function() { }); +}; + +Context.prototype.sayDate = function (seconds, escape_digits, cb) { //seconds since 1.01.1970 + this.send('SAY DATE ' + seconds + ' "' + escape_digits + '"' + '\n', cb || function() { }); +}; + +Context.prototype.sayTime = function (seconds, escape_digits, cb) { //seconds since 1.01.1970 + this.send('SAY TIME ' + seconds + ' "' + escape_digits + '"' + '\n', cb || function() { }); +}; + +Context.prototype.sayDateTime = function (seconds, escape_digits, format, timezone, cb) { //seconds since 1.01.1970 + this.send('SAY DATETIME ' + seconds + ' "' + escape_digits + '" ' + format + ' ' + timezone + '\n', cb || function() { }); +}; + +Context.prototype.sayDigits = function (digits, escape_digits, cb) { this.send('SAY DIGITS ' + digits + ' "' + escape_digits + '"' + '\n', cb || function() { }); }; -Context.prototype.sayPhonetic = function(string, escape_digits, cb) { +Context.prototype.sayPhonetic = function (string, escape_digits, cb) { this.send('SAY PHONETIC ' + string + ' "' + escape_digits + '"' + '\n', cb || function() { }); }; -Context.prototype.streamFile = function(filename, acceptDigits, cb) { +Context.prototype.streamFile = function (filename, acceptDigits, cb) { if(typeof acceptDigits === 'function') { cb = acceptDigits; acceptDigits = "1234567890#*"; @@ -154,7 +285,7 @@ Context.prototype.streamFile = function(filename, acceptDigits, cb) { this.send('STREAM FILE "' + filename + '" "' + acceptDigits + '"\n', cb); }; -Context.prototype.waitForDigit = function(timeout, cb) { +Context.prototype.waitForDigit = function (timeout, cb) { if(typeof timeout === 'function') { cb = timeout; //default to 2 second timeout @@ -163,11 +294,11 @@ Context.prototype.waitForDigit = function(timeout, cb) { this.send('WAIT FOR DIGIT ' + timeout + '\n', cb); }; -Context.prototype.hangup = function(cb) { +Context.prototype.hangup = function (cb) { this.send('HANGUP\n', cb); }; -Context.prototype.end = function() { +Context.prototype.end = function () { this.stream.end(); }; diff --git a/package.json b/package.json index 49be5ac..7837d7f 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "author": "Sergey Dmitriev ", "name": "ding-dong", - "description": "AGI (Asterisk Gateway Interface) for writing dialplan scripts", - "version": "0.0.7", + "description": "Write AGI-server quickly! (AGI - Asterisk Gateway Interface)", + "version": "0.0.9", "repository": { "type": "git", "url": "git://github.com/antirek/ding-dong.git" diff --git a/test/index.js b/test/index.js index 4860797..0f836ca 100644 --- a/test/index.js +++ b/test/index.js @@ -146,6 +146,90 @@ describe('Context', function() { }); }); + describe('databaseDel', function() { + it('sends correct command', function() { + this.context.databaseDel('family', 'test'); + expect(this.context.sent.join('')).to.eql('DATABASE DEL family test\n'); + }); + }); + + describe('databaseDelTree', function() { + it('sends correct command', function() { + this.context.databaseDelTree('family', 'test'); + expect(this.context.sent.join('')).to.eql('DATABASE DELTREE family test\n'); + }); + }); + + describe('databaseGet', function() { + it('sends correct command', function() { + this.context.databaseGet('family', 'test'); + expect(this.context.sent.join('')).to.eql('DATABASE GET family test\n'); + }); + }); + + describe('databasePut', function() { + it('sends correct command', function() { + this.context.databasePut('family', 'test', 'value'); + expect(this.context.sent.join('')).to.eql('DATABASE PUT family test value\n'); + }); + }); + + describe('speechCreate', function() { + it('sends correct command', function() { + this.context.speechCreate('engine'); + expect(this.context.sent.join('')).to.eql('SPEECH CREATE engine\n'); + }); + }); + + describe('speechDestroy', function() { + it('sends correct command', function() { + this.context.speechDestroy(); + expect(this.context.sent.join('')).to.eql('SPEECH DESTROY\n'); + }); + }); + + describe('speechActivateGrammar', function() { + it('sends correct command', function() { + this.context.speechActivateGrammar('name'); + expect(this.context.sent.join('')).to.eql('SPEECH ACTIVATE GRAMMAR name\n'); + }); + }); + + describe('speechDeactivateGrammar', function() { + it('sends correct command', function() { + this.context.speechDeactivateGrammar('name'); + expect(this.context.sent.join('')).to.eql('SPEECH DEACTIVATE GRAMMAR name\n'); + }); + }); + + describe('speechLoadGrammar', function() { + it('sends correct command', function() { + this.context.speechLoadGrammar('name', 'path'); + expect(this.context.sent.join('')).to.eql('SPEECH LOAD GRAMMAR name path\n'); + }); + }); + + describe('speechUnloadGrammar', function() { + it('sends correct command', function() { + this.context.speechUnloadGrammar('name'); + expect(this.context.sent.join('')).to.eql('SPEECH UNLOAD GRAMMAR name\n'); + }); + }); + + describe('speechSet', function() { + it('sends correct command', function() { + this.context.speechSet('name', 'value'); + expect(this.context.sent.join('')).to.eql('SPEECH SET name value\n'); + }); + }); + + describe('speechRecognize', function() { + it('sends correct command', function() { + this.context.speechRecognize('prompt', 'timeout', 'offset'); + expect(this.context.sent.join('')).to.eql('SPEECH RECOGNIZE prompt timeout offset\n'); + }); + }); + describe('setVariable', function() { it('sends correct command', function() { this.context.setVariable('test', 'test'); @@ -153,6 +237,48 @@ describe('Context', function() { }); }); + describe('setAutoHangup', function() { + it('sends correct command', function() { + this.context.setAutoHangup(10); + expect(this.context.sent.join('')).to.eql('SET AUTOHANGUP 10\n'); + }); + }); + + describe('setCallerID', function() { + it('sends correct command', function() { + this.context.setCallerID('246'); + expect(this.context.sent.join('')).to.eql('SET CALLERID 246\n'); + }); + }); + + describe('setContext', function() { + it('sends correct command', function() { + this.context.setContext('outbound'); + expect(this.context.sent.join('')).to.eql('SET CONTEXT outbound\n'); + }); + }); + + describe('setExtension', function() { + it('sends correct command', function() { + this.context.setExtension('245'); + expect(this.context.sent.join('')).to.eql('SET EXTENSION 245\n'); + }); + }); + + describe('setPriority', function() { + it('sends correct command', function() { + this.context.setPriority('2'); + expect(this.context.sent.join('')).to.eql('SET PRIORITY 2\n'); + }); + }); + + describe('setMusic', function() { + it('sends correct command', function() { + this.context.setMusic('default'); + expect(this.context.sent.join('')).to.eql('SET MUSIC default\n'); + }); + }); + describe('channelStatus', function() { it('sends correct command', function() { this.context.channelStatus('test'); @@ -167,6 +293,20 @@ describe('Context', function() { }); }); + describe('getData', function() { + it('sends correct command', function() { + this.context.getData('test', 10, 5); + expect(this.context.sent.join('')).to.eql('GET DATA test 10 5\n'); + }); + }); + + describe('getOption', function() { + it('sends correct command', function() { + this.context.getOption('test', '#', 5); + expect(this.context.sent.join('')).to.eql('GET OPTION test "#" 5\n'); + }); + }); + describe('getVariable', function() { it('sends correct command', function() { this.context.getVariable('test'); @@ -185,8 +325,22 @@ describe('Context', function() { }); }); + describe('receiveChar', function() { + it('sends correct command', function() { + this.context.receiveChar(5); + expect(this.context.sent.join('')).to.eql('RECEIVE CHAR 5\n'); + }); + }); + + describe('receiveText', function() { + it('sends correct command', function() { + this.context.receiveText(5); + expect(this.context.sent.join('')).to.eql('RECEIVE TEXT 5\n'); + }); + }); + describe('stream file', function() { - it('sends', function() { + it('sends', function () { this.context.streamFile('test', '1234567890#*', function() {}); expect(this.context.sent.join('')).to.eql('STREAM FILE "test" "1234567890#*"\n'); }); @@ -199,21 +353,49 @@ describe('Context', function() { }); describe('record file', function() { - it('record', function() { + it('record', function () { this.context.recordFile('test', 'wav', '#', 10, function() {}); expect(this.context.sent.join('')).to.eql('RECORD FILE "test" wav # 10000 0 1 2\n'); }); }); describe('say number', function() { - it('say number', function() { + it('say number', function () { this.context.sayNumber('1234', '#', function() {}); expect(this.context.sent.join('')).to.eql('SAY NUMBER 1234 "#"\n'); }); }); + describe('say alpha', function() { + it('say alpha', function () { + this.context.sayAlpha('1234', '#', function() {}); + expect(this.context.sent.join('')).to.eql('SAY ALPHA 1234 "#"\n'); + }); + }); + + describe('say date', function() { + it('say date', function () { + this.context.sayDate('1234', '#', function() {}); + expect(this.context.sent.join('')).to.eql('SAY DATE 1234 "#"\n'); + }); + }); + + describe('say time', function() { + it('say time', function () { + this.context.sayTime('1234', '#', function() {}); + expect(this.context.sent.join('')).to.eql('SAY TIME 1234 "#"\n'); + }); + }); + + describe('say datetime', function() { + it('say datetime', function () { + this.context.sayDateTime('1234', '#', 'Y', 'DST',function() {}); + expect(this.context.sent.join('')).to.eql('SAY DATETIME 1234 "#" Y DST\n'); + }); + }); + describe('say phonetic', function() { - it('say phonetic', function() { + it('say phonetic', function () { this.context.sayPhonetic('1234ABCD', '#', function() {}); expect(this.context.sent.join('')).to.eql('SAY PHONETIC 1234ABCD "#"\n'); }); @@ -227,13 +409,27 @@ describe('Context', function() { }); describe('say digits', function() { - it('say digits', function() { + it('say digits', function () { this.context.sayDigits('1234', '#', function() {}); expect(this.context.sent.join('')).to.eql('SAY DIGITS 1234 "#"\n'); }); }); - describe('waitForDigit', function() { + describe('send image', function() { + it('send image', function () { + this.context.sendImage('1234', function() {}); + expect(this.context.sent.join('')).to.eql('SEND IMAGE 1234\n'); + }); + }); + + describe('send text', function() { + it('send text', function () { + this.context.sendText('1234', function() {}); + expect(this.context.sent.join('')).to.eql('SEND TEXT "1234"\n'); + }); + }); + + describe('waitForDigit', function () { it('sends with default timeout', function() { this.context.waitForDigit(function() {}); expect(this.context.sent.join('')).to.eql('WAIT FOR DIGIT 5000\n'); @@ -266,6 +462,34 @@ describe('Context', function() { }); }); + describe('verbose', function() { + it('sends correct command', function() { + this.context.verbose("good", 2); + expect(this.context.sent.join('')).to.eql('VERBOSE "good" 2\n'); + }); + }); + + describe('tddMode', function() { + it('sends correct command', function() { + this.context.tddMode("on"); + expect(this.context.sent.join('')).to.eql('TDD MODE on\n'); + }); + }); + + describe('noop', function() { + it('sends correct command', function() { + this.context.noop(); + expect(this.context.sent.join('')).to.eql('NOOP\n'); + }); + }); + + describe('gosub', function() { + it('sends correct command', function() { + this.context.gosub('out','241','6','do'); + expect(this.context.sent.join('')).to.eql('GOSUB out 241 6 do\n'); + }); + }); + describe('events', function() { describe('error', function () { it('is emitted when socket emits error', function(done) { @@ -305,4 +529,4 @@ describe('agi#createServer', function() { server.emit('connection', new MemoryStream()); }); -}); +}); \ No newline at end of file