├── AUTHORS ├── LICENSE ├── README.md ├── asterisk.js └── test.js /AUTHORS: -------------------------------------------------------------------------------- 1 | # Authors ordered by first contribution. 2 | 3 | Brian White -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2010 Brian White . All rights reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 4 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 5 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 10 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 11 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 12 | IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Description 2 | =========== 3 | 4 | node-asterisk is a [node.js](http://nodejs.org/) module that allows interaction with an Asterisk server. 5 | See the test.js script for example usage. 6 | 7 | 8 | Requirements 9 | ============ 10 | 11 | * [node.js](http://nodejs.org/) -- tested with v0.1.95 12 | * An [Asterisk](http://www.asterisk.org/) server -- tested with v1.4.21.2 13 | 14 | 15 | API Documentation 16 | ================= 17 | 18 | node-asterisk exposes only one class: **AsteriskManager**. 19 | 20 | 21 | #### Data types 22 | 23 | * _Participant_ is an object currently containing the following properties: 24 | * **name** - A String containing the name provided by Caller ID, if it's not available/provided then it's set to "<unknown>". **Note:** Caller ID _name_ information is only available once a call is connected. 25 | * **number** - An Integer representing a 10-digit (PSTN) landline number or an asterisk extension. 26 | * **with** - A String representing a unique number identifying another Participant they are associated with. **Note:** this property is not set until the _dialing_ event occurs. 27 | 28 | 29 | AsteriskManager Events 30 | ---------------------- 31 | 32 | * **serverconnect**() - Fires when a connection to the asterisk server has been successfuly established. 33 | 34 | * **servererror**(String) - Fires when a Javascript error occurs. The given String represents the text of the error. 35 | 36 | * **serverdisconnect**(Boolean) - Fires when a connection to the asterisk server has been lost, with the given Boolean indicating whether the disconnection was the result of an error. 37 | 38 | * **dialing**(Participant, Participant) - Fires when the first Participant is calling the second Participant. 39 | 40 | * **callconnected**(Participant, Participant) - Fires when the first Participant is successfully connected to the second Participant and voice can be exchanged. 41 | 42 | * **calldisconnected**(Participant, Participant) - Fires when the two Participants are disconnected from each other for some reason (normal or otherwise). 43 | 44 | * **hangup**(Participant, Integer, String) - Fires for each Participant in the _dialing_ event, with the second parameter being the [cause code](http://www.voip-info.org/wiki/view/asterisk+manager+events#HangupEvent) and the third parameter being a human-readable version of the cause code. **Note:** This event will fire soon after the _dialing_ event if either Participant has a busy signal. 45 | 46 | * **hold**(Participant) - Fires when the given Participant has put the other Participant they are connected with, on hold. 47 | 48 | * **unhold**(Participant) - Fires when the given Participant has taken the other Participant they are connected with, off of hold. 49 | 50 | * **callreport**(Object) - Fires at the end of any call attempt (successful or otherwise). The given object contains some useful information about the call, including: 51 | * **caller** - The Participant object of the originator of the call. 52 | * **callee** - The Participant object of the receiver of the call. 53 | * **startTime** - A String containing the date and time (according to the local clock -- i.e. not converted to GMT) that dialing started, in this format: "2010-05-18 22:52:45". 54 | * **answerTime** - A String containing the date and time (according to the local clock -- i.e. not converted to GMT) the call was answered, in this format: "2010-05-18 22:52:45". If the call was unsuccessful, this is simply a blank string. 55 | * **endTime** - A String containing the date and time (according to the local clock -- i.e. not converted to GMT) the call attempt ended, in this format: "2010-05-18 22:52:45". 56 | * **totalDuration** - An Integer representing the total number of seconds from the time of dialing to the end of the call (using _calldisconnected_ as a reference for a connected call or _hangup_ for an unsuccessful call). 57 | * **talkDuration** - An Integer representing the total number of seconds from the time the call was successfully connected to the time the call was disconnected. 58 | * **finalStatus** - A String containing a description of the status of the call (i.e. "no answer", "busy", "answered", etc). 59 | 60 | 61 | AsteriskManager Functions 62 | ------------------------- 63 | 64 | * **(constructor)**([Object]) - _AsteriskManager_ - Creates and returns a new instance of AsteriskManager using the specified configuration object. At least the 'user' and 'password' config properties must be specified. Valid properties of the passed in object are: 65 | * **user** - A String representing the username to log into the asterisk server with. 66 | * **password** - A String representing the password associated with the user to log into the asterisk server with. 67 | * **host** - A String representing the hostname or IP address of the asterisk server. **Default:** "localhost" 68 | * **port** - An Integer representing the port of the asterisk server. **Default:** 5038 69 | * **events** - A String indicating what classes of events you wish to listen for ("on" for all events, "off" for no events, or a comma-delimited String containing the specific names of events to listen for). Usually you do not want to change this, except if you are only going to be issuing commands and don't need to listen for events, in which case set this to "off". A list of valid event classes can be found [here](http://www.voip-info.org/wiki/view/Asterisk+manager+API) under "Authorization for various classes". **Default:** "on" 70 | * **connect_timeout** - An Integer representing the time to wait for a connection to the Asterisk server (in milliseconds). Zero indicates no timeout. **Default:** 0 71 | 72 | * **connect**() - _(void)_ - Attempts to connect to the asterisk server. 73 | 74 | * **login**() - _(void)_ - Performs authentication. 75 | 76 | * **getParticipant**(String) - _Participant_ - Retrieves the Participant with the specified unique id. For now, these ids only exist in the _with_ property of Participant. **Note:** Participants are non-existant after the appropriate _callreport_ event is fired. -------------------------------------------------------------------------------- /asterisk.js: -------------------------------------------------------------------------------- 1 | var inherits = require('sys').inherits; 2 | var EventEmitter = require('events').EventEmitter; 3 | var net = require('net'); 4 | 5 | var CRLF = "\r\n"; 6 | var END = "\r\n\r\n"; 7 | 8 | exports.AsteriskManager = function (newconfig) { 9 | EventEmitter.call(this); 10 | var default_config = { 11 | user: null, 12 | password: null, 13 | host: 'localhost', 14 | port: 5038, 15 | events: 'on', 16 | connect_timeout: 0 // the time to wait for a connection to the Asterisk server (in milliseconds) 17 | }; 18 | var config; 19 | var tmoConn = null; 20 | var conn = null; 21 | var self = this; 22 | var loggedIn_ = false; 23 | var loginId = null; 24 | var buffer = ""; 25 | 26 | var actions = {}; 27 | var partcipants = {}; 28 | 29 | this.setConfig = function(newconfig) { 30 | config = {}; 31 | for (var option in default_config) 32 | config[option] = (typeof newconfig[option] != "undefined" ? newconfig[option] : default_config[option]); 33 | }; 34 | 35 | this.send = function(req, cb) { 36 | var id = (new Date()).getTime(); 37 | actions[id] = {request: req, callback: cb}; 38 | var msg = ""; 39 | for (var key in req) 40 | msg += key + ": " + req[key] + CRLF; 41 | msg += "actionid: " + id + CRLF + CRLF; 42 | if (req.action == 'login') 43 | loginId = id; 44 | self.conn.write(msg); 45 | }; 46 | 47 | this.getParticipant = function(id) { 48 | return self.participants[id]; 49 | } 50 | 51 | this.OnConnect = function() { 52 | self.participants = {}; 53 | if (config.connect_timeout > 0) 54 | clearTimeout(self.tmoConn); 55 | self.emit('serverconnect'); 56 | }; 57 | 58 | this.OnError = function(err) { 59 | self.conn.end(); 60 | self.emit('servererror', err); 61 | }; 62 | 63 | this.OnClose = function(had_error) { 64 | self.emit('serverdisconnect', had_error); 65 | self.conn.destroy(); 66 | loggedIn_ = false; 67 | }; 68 | 69 | this.OnEnd = function() { 70 | self.conn.end(); 71 | this.OnClose(false); 72 | }; 73 | 74 | this.OnData = function(tcpbuffer) { 75 | data = tcpbuffer.toString(); 76 | if (data.substr(0, 21) == "Asterisk Call Manager") 77 | data = data.substr(data.indexOf(CRLF)+2); // skip the server greeting when first connecting 78 | buffer += data; 79 | var iDelim, info, headers, kv, type; 80 | while ((iDelim = buffer.indexOf(END)) > -1) { 81 | info = buffer.substring(0, iDelim+2).split(CRLF); 82 | buffer = buffer.substr(iDelim + 4); 83 | headers = {}; type = ""; kv = []; 84 | for (var i=0,len=info.length; i 0) { 179 | self.tmoConn = setTimeout(function() { 180 | self.emit('timeout'); 181 | self.conn.end(); 182 | }, config.connect_timeout); 183 | } 184 | } 185 | }; 186 | 187 | this.login = function(cb) { 188 | if (!loggedIn_ && self.conn.readyState == 'open') { 189 | self.send({ 190 | action: 'login', 191 | username: config.user, 192 | secret: config.password, 193 | events: config.events 194 | }, cb); 195 | } 196 | }; 197 | 198 | this.disconnect = function() { 199 | if (self.conn.readyState == 'open') 200 | self.conn.end(); 201 | }; 202 | 203 | this.__defineGetter__('loggedIn', function () { return loggedIn_; }); 204 | 205 | this.setConfig(newconfig); 206 | }; 207 | 208 | inherits(exports.AsteriskManager, EventEmitter); -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'), ast = require('./asterisk'); 2 | 3 | am = new ast.AsteriskManager({user: 'foo', password: 'bar'}); 4 | 5 | am.addListener('serverconnect', function() { 6 | sys.puts("CLIENT: Connected!"); 7 | am.login(function () { 8 | sys.puts("CLIENT: Logged in!"); 9 | }); 10 | }); 11 | 12 | am.addListener('serverdisconnect', function(had_error) { 13 | sys.puts("CLIENT: Disconnected! had_error == " + (had_error ? "true" : "false")); 14 | }); 15 | 16 | am.addListener('servererror', function(err) { 17 | sys.puts("CLIENT: Error: " + err); 18 | }); 19 | 20 | am.addListener('dialing', function(from, to) { 21 | sys.puts("CLIENT: Dialing from " + from.number + " (" + from.name + ") to " + to.number + " (" + to.name + ")"); 22 | }); 23 | 24 | am.addListener('callconnected', function(from, to) { 25 | sys.puts("CLIENT: Connected call between " + from.number + " (" + from.name + ") and " + to.number + " (" + to.name + ")"); 26 | }); 27 | 28 | am.addListener('calldisconnected', function(from, to) { 29 | sys.puts("CLIENT: Disconnected call between " + from.number + " (" + from.name + ") and " + to.number + " (" + to.name + ")"); 30 | }); 31 | 32 | am.addListener('hold', function(participant) { 33 | var other = am.getParticipant(participant['with']); 34 | sys.puts("CLIENT: " + participant.number + " (" + participant.name + ") has put " + other.number + " (" + other.name + ") on hold"); 35 | }); 36 | 37 | am.addListener('unhold', function(participant) { 38 | var other = am.getParticipant(participant['with']); 39 | sys.puts("CLIENT: " + participant.number + " (" + participant.name + ") has taken " + other.number + " (" + other.name + ") off hold"); 40 | }); 41 | 42 | am.addListener('hangup', function(participant, code, text) { 43 | var other = am.getParticipant(participant['with']); 44 | sys.puts("CLIENT: " + participant.number + " (" + participant.name + ") has hung up. Reason: " + code + " (" + text + ")"); 45 | }); 46 | 47 | am.addListener('callreport', function(report) { 48 | sys.puts("CLIENT: Call report: " + sys.inspect(report)); 49 | }); 50 | 51 | am.connect(); --------------------------------------------------------------------------------