From 488503bdf113fe080349864fccd21f4e319f2550 Mon Sep 17 00:00:00 2001 From: Miguel Angel Date: Sun, 9 Jun 2024 19:53:08 -0500 Subject: [PATCH] Improvements and Updates in the Code. Converted to ES6 class 1. **message.js**: - Converted to ES6 class. - Updated methods to use class method syntax. 2. **action.js**: - Converted to ES6 class. - Implemented `super()` for calls to the parent class constructor. - Dynamic generation of action classes using `createActionClass`. - Exported dynamically generated classes. 3. **response.js**: - Converted to ES6 class. - Implemented `super()` for calls to the parent class constructor. - Updated to export the `Response` class. 4. **event.js**: - Converted to ES6 class. - Implemented `super()` for calls to the parent class constructor. - Updated to export the `Event` class. --- src/message/action.js | 1420 ++++----------------------------------- src/message/event.js | 35 +- src/message/message.js | 125 +--- src/message/response.js | 37 +- src/nami.js | 279 +++----- 5 files changed, 284 insertions(+), 1612 deletions(-) diff --git a/src/message/action.js b/src/message/action.js index 016951b..2b2f00b 100644 --- a/src/message/action.js +++ b/src/message/action.js @@ -16,1305 +16,155 @@ * limitations under the License. * */ -/** - * @fileoverview Base action class. +/*! + * Action message. * - * @author Marcelo Gornstein - http://marcelog.github.com - * Website: http://marcelog.github.com/Nami - */ -message = require(__dirname + '/message.js'); -util = require('util'); - -/** - * Base action class. Every action sent to AMI must be one of these. - * @constructor - * @param {String} name The name of the action, this is the actual value of the - * "Action" key in the action message. - * @see Message#marshall(String) - * @augments Message - */ -function Action(name) { - Action.super_.call(this); - this.id = ActionUniqueId(); - this.set('ActionID', this.id); - this.set('Action', name); -} - -var ActionUniqueId = (function() { - var nextId = 0; - return function() { - return nextId++; - }; -})(); - -/** - * Login Action. - * @constructor - * @param {String} username The username. The value of the "Username" key. - * @param {String} secret The password. The value of the "Secret" key. - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_Login. - * @augments Action - */ -function Login(username, secret) { - Login.super_.call(this, 'Login'); - this.set('Username', username); - this.set('Secret', secret ); -} -/** - * CoreShowChannels Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_CoreShowChannels. - * @augments Action - */ -function CoreShowChannels() { - CoreShowChannels.super_.call(this, 'CoreShowChannels'); -} - -/** - * Ping Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_Ping. - * @augments Action - */ -function Ping() { - Ping.super_.call(this, 'Ping'); -} - -/** - * Hangup Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_Hangup. - * @augments Action - * @property {String} Channel Channel to hangup. - */ -function Hangup() { - Hangup.super_.call(this, 'Hangup'); -} - -/** - * CoreStatus Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_CoreStatus. - * @augments Action - */ -function CoreStatus() { - CoreStatus.super_.call(this, 'CoreStatus'); -} - -/** - * Status Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_Status. - * @augments Action - * @property {String} Channel Optional channel to get status from. Do not set this property - * if you want to get all channels - */ -function Status() { - Status.super_.call(this, 'Status'); -} - -/** - * DahdiShowChannels Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_DAHDIShowChannels. - * @augments Action - */ -function DahdiShowChannels() { - DahdiShowChannels.super_.call(this, 'DahdiShowChannels'); -} - -/** - * CoreSettings Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_CoreSettings. - * @augments Action - */ -function CoreSettings() { - CoreSettings.super_.call(this, 'CoreSettings'); -} - -/** - * ListCommands Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_ListCommands. - * @augments Action - */ -function ListCommands() { - ListCommands.super_.call(this, 'ListCommands'); -} - -/** - * Logoff Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_Logoff. - * @augments Action - */ -function Logoff() { - Logoff.super_.call(this, 'Logoff'); -} - -/** - * AbsoluteTimeout Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_AbsoluteTimeout. - * @augments Action - * @property {String} Channel to hangup. - * @property {Integer} Timeout in seconds. - */ -function AbsoluteTimeout() { - AbsoluteTimeout.super_.call(this, 'AbsoluteTimeout'); -} - -/** - * SIPShowPeer Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_SIPshowpeer. - * @augments Action - * @property {String} Peer SIP peer name - */ -function SipShowPeer() { - SipShowPeer.super_.call(this, 'SIPshowpeer'); -} - -/** - * SIPShowRegistry Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_SIPshowregistry. - * @augments Action - */ -function SipShowRegistry() { - SipShowRegistry.super_.call(this, 'SIPshowregistry'); -} - -/** - * SIPQualifyPeer Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_SIPqualifypeer. - * @augments Action - * @property {String} Peer SIP peer name - */ -function SipQualifyPeer() { - SipQualifyPeer.super_.call(this, 'SIPqualifypeer'); -} - -/** - * SIPPeers Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_SIPpeers. - * @augments Action - */ -function SipPeers() { - SipPeers.super_.call(this, 'SIPpeers'); -} - -/** - * AgentLogoff Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_AgentLogoff. - * @property {String} Agent Agent name - * @property {String} Soft Set to true to not hangup existing calls - * @augments Action - */ -function AgentLogoff() { - AgentLogoff.super_.call(this, 'AgentLogoff'); -} - -/** - * Agents Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_Agents. - * @augments Action - */ -function Agents() { - Agents.super_.call(this, 'Agents'); -} - -/** - * AttendedTransfer Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_Atxfer. - * @property {String} Channel Channel to transfer - * @property {String} Exten Extension to transfer to - * @property {String} Context Context to transfer to - * @property {String} Priority Priority to transfer to - * @augments Action - */ -function AttendedTransfer() { - AttendedTransfer.super_.call(this, 'Atxfer'); -} - -/** - * ChangeMonitor Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_ChangeMonitor. - * @property {String} Channel Channel to monitor - * @property {String} File File where to save the audio - * @augments Action - */ -function ChangeMonitor() { - ChangeMonitor.super_.call(this, 'ChangeMonitor'); -} - -/** - * Command Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_Command. - * @property {String} Command Command to send - * @augments Action - */ -function Command() { - Command.super_.call(this, 'Command'); -} - -/** - * CreateConfig Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_CreateConfig. - * @property {String} Filename Filename to create - * @augments Action - */ -function CreateConfig() { - CreateConfig.super_.call(this, 'CreateConfig'); -} - -/** - * DahdiDialOffHook Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_DAHDIDialOffhook. - * @property {String} DAHDIChannel Dahdi Channel to use - * @property {String} Number Number to dial - * @augments Action - */ -function DahdiDialOffHook() { - DahdiDialOffHook.super_.call(this, 'DahdiDialOffHook'); -} - -/** - * DahdiDndOff Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_DAHDIDNDOff. - * @property {String} DAHDIChannel Dahdi Channel - * @augments Action - */ -function DahdiDndOff() { - DahdiDndOff.super_.call(this, 'DahdiDndOff'); -} - -/** - * DahdiDndOn Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_DAHDIDNDOn. - * @property {String} DAHDIChannel Dahdi Channel - * @augments Action - */ -function DahdiDndOn() { - DahdiDndOn.super_.call(this, 'DahdiDndOn'); -} - -/** - * DahdiHangup Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_DAHDIHangup. - * @property {String} DAHDIChannel Dahdi Channel - * @augments Action - */ -function DahdiHangup() { - DahdiHangup.super_.call(this, 'DahdiHangup'); -} - -/** - * DahdiRestart Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_DAHDIRestart. - * @augments Action - */ -function DahdiRestart() { - DahdiRestart.super_.call(this, 'DahdiRestart'); -} - -/** - * DbDel Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_DbDel. - * @property {String} Family Key Family - * @property {String} Key Key Name - * @augments Action - */ -function DbDel() { - DbDel.super_.call(this, 'DbDel'); -} - -/** - * DbDeltree Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_DbDeltree. - * @property {String} Family Key Family - * @property {String} Key Optional Key Name - * @augments Action - */ -function DbDeltree() { - DbDeltree.super_.call(this, 'DbDeltree'); -} - -/** - * DbGet Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_DbGet. - * @property {String} Family Key Family - * @property {String} Key Key Name - * @augments Action - */ -function DbGet() { - DbGet.super_.call(this, 'DbGet'); -} - -/** - * DbPut Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_DbPut. - * @property {String} Family Key Family - * @property {String} Key Key Name - * @property {String} Value Value - * @augments Action - */ -function DbPut() { - DbPut.super_.call(this, 'DbPut'); -} - -/** - * ExtensionState Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_ExtensionState. - * @property {String} Exten Extension - * @property {String} Context Context of the extension - * @augments Action - */ -function ExtensionState() { - ExtensionState.super_.call(this, 'ExtensionState'); -} - -/** - * GetConfig Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_GetConfig. - * @property {String} Filename File to get configuration from. - * @property {String} Category Optional category to retrieve. - * @augments Action - */ -function GetConfig() { - GetConfig.super_.call(this, 'GetConfig'); -} - -/** - * GetConfigJson Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_GetConfigJson. - * @property {String} Filename File to get configuration from. - * @augments Action - */ -function GetConfigJson() { - GetConfigJson.super_.call(this, 'GetConfigJson'); -} - -/** - * GetVar Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_GetVar. - * @property {String} Variable Variable Name - * @property {String} Channel Optional Channel name. - * @augments Action - */ -function GetVar() { - GetVar.super_.call(this, 'GetVar'); -} - -/** - * PJSIPNotify Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/Asterisk+14+ManagerAction_PJSIPNotify. - * @property {String} The endpoint to which to send the NOTIFY - * @property {String} Abritrary URI to which to send the NOTIFY - * @property {String} Appends variables as headers/content to the NOTIFY - * @augments Action - */ -function PJSIPNotify() { - PJSIPNotify.super_.call(this, 'PJSIPNotify'); -} - -/** - * JabberSend Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_JabberSend. - * @property {String} Jabber Client or transport Asterisk uses to connect to JABBER - * @property {String} JID XMPP/Jabber JID (Name) of recipient - * @property {String} Message Message to be sent to the buddy - * @augments Action - */ -function JabberSend() { - JabberSend.super_.call(this, 'JabberSend'); -} - -/** - * ListCategories Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_ListCategories. - * @property {String} Filename File to get categories from. - * @augments Action - */ -function ListCategories() { - ListCategories.super_.call(this, 'ListCategories'); -} - -/** - * PauseMonitor Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_PauseMonitor. - * @property {String} Channel Pause monitor on this channel - * @augments Action - */ -function PauseMonitor() { - PauseMonitor.super_.call(this, 'PauseMonitor'); -} - -/** - * UnpauseMonitor Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_UnpauseMonitor. - * @property {String} Channel Continue monitor this channel - * @augments Action - */ -function UnpauseMonitor() { - UnpauseMonitor.super_.call(this, 'UnpauseMonitor'); -} - -/** - * StopMonitor Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_StopMonitor. - * @property {String} Channel Stop monitor this channel - * @augments Action - */ -function StopMonitor() { - StopMonitor.super_.call(this, 'StopMonitor'); -} - - -/** - * LocalOptimizeAway Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_LocalOptimizeAway. - * @property {String} Channel Channel - * @augments Action - */ -function LocalOptimizeAway() { - LocalOptimizeAway.super_.call(this, 'LocalOptimizeAway'); -} - -/** - * SetVar Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_SetVar. - * @property {String} Variable Variable Name - * @property {String} Value Variable Value - * @property {String} Channel Optional Channel name. - * @augments Action - */ -function SetVar() { - SetVar.super_.call(this, 'SetVar'); -} - -/** - * Reload Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_Reload. - * @property {String} Module Optional module name - * @augments Action - */ -function Reload() { - Reload.super_.call(this, 'Reload'); -} - -/** - * PlayDtmf Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_PlayDtmf. - * @property {String} Channel Channel where to play the dtmf - * @property {String} Digit DTMF digit to play - * @augments Action - */ -function PlayDtmf() { - PlayDtmf.super_.call(this, 'PlayDtmf'); -} - -/** - * Park Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_Park. - * @property {String} Channel Channel name to park - * @property {String} Channel2 Channel to announce park info to (and return to if timeout) - * @property {String} Timeout Optional number of milliseconds to wait before callback - * @property {String} Parkinglot Optional parking lot to park channel in - * @augments Action - */ -function Park() { - Park.super_.call(this, 'Park'); -} - -/** - * ParkedCalls Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+ManagerAction_ParkedCalls. - * @property {String} ParkingLot Optional parking lot to view - * @augments Action - */ -function ParkedCalls(lot) { - ParkedCalls.super_.call(this, 'ParkedCalls'); - - if (undefined !== lot) { - this.set('ParkingLot', lot); - } - -} - -/** - * Parkinglots Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+ManagerAction_Parkinglots. - * @augments Action - */ -function Parkinglots() { - Parkinglots.super_.call(this, 'Parkinglots'); -} - -/** - * Monitor Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_Monitor. - * @property {String} Channel Channel name to park - * @property {String} Filename Path where to save the audio - * @augments Action - */ -function Monitor() { - Monitor.super_.call(this, 'Monitor'); - this.format = 'wav'; - this.mix = 'true'; -} - -/** - * ModuleCheck Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_ModuleCheck. - * @property {String} Module Module name, including .so - * @augments Action - */ -function ModuleCheck() { - ModuleCheck.super_.call(this, 'ModuleCheck'); -} - -/** - * ModuleLoad Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_ModuleLoad. - * @property {String} Module Module name, including .so - * @augments Action - */ -function ModuleLoad() { - ModuleLoad.super_.call(this, 'ModuleLoad'); - this.LoadType = 'load'; -} - -/** - * ModuleUnload Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_ModuleUnload. - * @property {String} Module Module name, including .so - * @augments Action - */ -function ModuleUnload() { - ModuleUnload.super_.call(this, 'ModuleUnload'); - this.LoadType = 'unload'; -} - -/** - * ModuleReload Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_ModuleReload. - * @property {String} Module Module name, including .so - * @augments Action - */ -function ModuleReload() { - ModuleReload.super_.call(this, 'ModuleReload'); - this.LoadType = 'reload'; -} - -/** - * MailboxCount Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_MailboxCount. - * @property {String} Mailbox Mailbox to retrieve count from - * @augments Action - */ -function MailboxCount() { - MailboxCount.super_.call(this, 'MailboxCount'); -} - -/** - * MailboxStatus Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_MailboxStatus. - * @property {String} Mailbox Mailbox to retrieve count from - * @augments Action - */ -function MailboxStatus() { - MailboxStatus.super_.call(this, 'MailboxStatus'); -} - -/** - * VoicemailUsersList Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_VoicemailUsersList. - * @augments Action - */ -function VoicemailUsersList() { - VoicemailUsersList.super_.call(this, 'VoicemailUsersList'); -} - -/** - * Redirect Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_Redirect. - * @augments Action - * @property {String} Channel Channel to transfer - * @property {String} Exten Extension to transfer to - * @property {String} Context Context to transfer to - * @property {String} Priority Priority to transfer to - * @property {String} ExtraChannel Optional Second call leg to transfer - * @property {String} ExtraExten Optional Extension to transfer extra channel - * @property {String} ExtraContext Optional Context to transfer extra channel - * @property {String} ExtraPriority Optional Priority to transfer extra channel - */ -function Redirect() { - Redirect.super_.call(this, 'Redirect'); -} - -/** - * Bridge Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_Bridge. - * @augments Action - * @property {String} Channel1 Channel to Bridge to Channel2 - * @property {String} Channel2 Channel to Bridge to Channel1 - * @property {String} Tone Play courtesy tone to Channel 2 [yes/no] - */ -function Bridge() { - Bridge.super_.call(this, 'Bridge'); -} - -/** - * ShowDialPlan Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_ShowDialPlan. - * @property {String} Context Optional context to list - * @property {String} Extension Optional extension to list - * @augments Action - */ -function ShowDialPlan() { - ShowDialPlan.super_.call(this, 'ShowDialPlan'); -} - -/** - * SendText Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_SendText. - * @property {String} Channel Channel where to send the message - * @property {String} Message Message to send - * @augments Action - */ -function SendText() { - SendText.super_.call(this, 'SendText'); -} - -/** - * Queues Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_Queues. - * @augments Action + * Licensed under the Apache License, Version 2.0 (the "License"); */ -function Queues() { - Queues.super_.call(this, 'Queues'); -} /** - * QueueReload Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_QueueReload. - * @property {String} Queue Optional, Queue - * @property {String} Members Optional, yes/no - * @property {String} Rules Optional, yes/no - * @property {String} Parameters Optional, yes/no - * @augments Action - */ -function QueueReload(queue, members, rules, parameters) { - QueueReload.super_.call(this, 'QueueReload'); - - if (undefined !== queue) { - this.set('queue', queue); - } - - if (undefined !== members) { - this.set('members', members); - } - - if (undefined !== rules) { - this.set('rules', rules); - } - - if (undefined !== parameters) { - this.set('parameters', parameters); - } -} - -/** - * QueueUnpause Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_QueuePause. - * @property {String} Interface Interface - * @property {String} Queue Optional, Queue - * @property {String} Reason Optional, reason description - * @augments Action + * @fileoverview Base action class. */ -function QueueUnpause(asteriskInterface, queue, reason) { - QueueUnpause.super_.call(this, 'QueuePause'); - this.set('paused', 'false'); - this.set('interface', asteriskInterface); - if (undefined !== queue) { - this.set('queue', queue); - } - - if (undefined !== reason) { - this.set('reason', reason); - } -} +const { Message } = require(__dirname + '/message.js'); /** - * QueueUnpause Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_QueuePause. - * @property {String} Interface Interface - * @property {String} Queue Optional, Queue - * @property {String} Reason Optional, reason description - * @augments Action + * Generates a unique ID for actions. */ -function QueuePause(asteriskInterface, queue, reason) { - QueuePause.super_.call(this, 'QueuePause'); - this.set('paused', 'true'); - this.set('interface', asteriskInterface); - - if (undefined !== queue) { - this.set('queue', queue); - } - - if (undefined !== reason) { - this.set('reason', reason); - } -} +const ActionUniqueId = (() => { + let nextId = 0; + return () => nextId++; +})(); /** - * QueueSummary Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_QueueSummary. - * @property {String} Queue Optional, Queue - * @augments Action + * Base action class. Every action sent to AMI must be one of these. + * @extends Message */ -function QueueSummary(queue) { - QueueSummary.super_.call(this, 'QueueSummary'); - if (undefined !== queue) { - this.set('Queue', queue); +class Action extends Message { + constructor(name) { + super(); + this.id = ActionUniqueId(); + this.set('ActionID', this.id); + this.set('Action', name); } } /** - * QueueRule Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_QueueRule. - * @property {String} Rule Optional, Rule - * @augments Action - */ -function QueueRule() { - QueueRule.super_.call(this, 'QueueRule'); -} - -/** - * QueueStatus Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_QueueStatus. - * @property {String} Queue Optional, Queue - * @property {String} Member Optional, Member - * @augments Action - */ -function QueueStatus(queue, member) { - QueueStatus.super_.call(this, 'QueueStatus'); - if (undefined !== queue) { - this.set('Queue', queue); - } - if (undefined !== member) { - this.set('Member', member); - } -} - -/** - * QueueReset Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_QueueReset. - * @property {String} Queue Optional, Queue - * @augments Action - */ -function QueueReset() { - QueueReset.super_.call(this, 'QueueReset'); -} - -/** - * QueueRemove Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_QueueRemove. - * @property {String} Queue Queue - * @property {String} Interface Interface - * @augments Action - */ -function QueueRemove(asteriskInterface, queue) { - QueueRemove.super_.call(this, 'QueueRemove'); - this.set('interface', asteriskInterface); - this.set('queue', queue); -} - -/** - * Originate Action - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_Originate. - * @property {String} Channel Channel - * @property {String} Exten Exten - * @property {String} Priority Priority - * @property {String} Application Application - * @property {String} Data Data - * @property {String} Timeout Timeout - * @property {String} CallerID CallerID - * @property {String} Account Account - * @property {String} Async Async - * @property {String} Codecs Codecs - * @augments Action - */ -function Originate() { - Originate.super_.call(this, 'Originate'); -} - -/** - * QueueAdd Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_QueueAdd. - * @property {String} Queue Queue - * @property {String} Interface Interface - * @property {String} Paused Optional, 'true' or 'false - * @property {String} MemberName Optional, Member name - * @property {String} Penalty Optional, Penalty - * @property {String} StateInterface Optional, State interface - * @augments Action - */ -function QueueAdd(asteriskInterface, queue, paused, memberName, penalty) { - QueueAdd.super_.call(this, 'QueueAdd'); - this.set('interface', asteriskInterface); - this.set('queue', queue); - if (undefined !== paused) { - this.set('paused', paused); - } - if (undefined !== memberName) { - this.set('membername', memberName); - } - if (undefined !== penalty) { - this.set('penalty', penalty); - } -} - -/** - * QueueLog Action. - * @constructor - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/ManagerAction_QueueLog. - * @property {String} Queue Queue - * @property {String} Event Event - * @property {String} Message Optional, Message - * @property {String} Interface Optional, Interface - * @property {String} UniqueId Optional, UniqueId - * @augments Action - */ -function QueueLog() { - QueueLog.super_.call(this, 'QueueLog'); -} - -/** - * MeetmeList Action. - * @constructor - * @see Action(String) - * @augments Action - */ - function MeetmeList(conference) { - MeetmeList.super_.call(this, 'MeetmeList'); - if(conference !== null) { - this.set('Conference', conference); - } - } - - /** - * MeetmeMute Action. - * @constructor - * @see Action(String) - * @augments Action - */ - function MeetmeMute(meetme, usernum) { - MeetmeMute.super_.call(this, 'MeetmeMute'); - this.set('Meetme', meetme); - this.set('Usernum', usernum); - } - -/** - * MeetmeUnmute Action. - * @constructor - * @see Action(String) - * @augments Action - */ -function MeetmeUnmute(meetme, usernum) { - MeetmeUnmute.super_.call(this, 'MeetmeUnmute'); - this.set('Meetme', meetme); - this.set('Usernum', usernum); -} - -/** - * ConfbridgeListRooms Action. - * @constructor - * @see Action(String) - * @augments Action - */ -function ConfbridgeListRooms() { - ConfbridgeListRooms.super_.call(this, 'ConfbridgeListRooms'); -} - -/** - * ConfbridgeList Action. - * @constructor - * @see Action(String) - * @param {String} conference room. The value of the "conference" key. - * @augments Action - */ -function ConfbridgeList(conference) { - ConfbridgeList.super_.call(this, 'ConfbridgeList'); - this.set('Conference', conference); -} - -/** - * ConfbridgeKick Action. - * @constructor - * @see Action(String) - * @param {String} conference room. The value of the "conference" key. - * @param {String} Channel. The value of the "Channel" key. - * @augments Action - */ -function ConfbridgeKick(conference, channel) { - ConfbridgeKick.super_.call(this, 'ConfbridgeKick'); - this.set('Conference', conference); - this.set('Channel', channel); -} - -/** - * ConfbridgeLock Action. - * @constructor - * @see Action(String) - * @param {String} conference room. The value of the "conference" key. - * @augments Action - */ -function ConfbridgeLock(conference) { - ConfbridgeLock.super_.call(this, 'ConfbridgeLock'); - this.set('Conference', conference); -} - -/** - * ConfbridgeUnlock Action. - * @constructor - * @see Action(String) - * @param {String} conference room. The value of the "conference" key. - * @augments Action - */ -function ConfbridgeUnlock(conference) { - ConfbridgeUnlock.super_.call(this, 'ConfbridgeUnlock'); - this.set('Conference', conference); -} - -/** - * ConfbridgeMute Action. - * @constructor - * @see Action(String) - * @param {String} conference room. The value of the "conference" key. - * @param {String} Channel. The value of the "Channel" key. - * @augments Action - */ -function ConfbridgeMute(conference, channel) { - ConfbridgeMute.super_.call(this, 'ConfbridgeMute'); - this.set('Conference', conference); - this.set('Channel', channel); -} - -/** - * ConfbridgeUnmute Action. - * @constructor - * @see Action(String) - * @param {String} conference room. The value of the "conference" key. - * @param {String} Channel. The value of the "Channel" key. - * @augments Action - */ -function ConfbridgeUnmute(conference, channel) { - ConfbridgeUnmute.super_.call(this, 'ConfbridgeUnmute'); - this.set('Conference', conference); - this.set('Channel', channel); -} - -/** - * AGI Action. - * @constructor - * @see Action(String) - * @see https://wiki.asterisk.org/wiki/display/AST/ManagerAction_AGI - * @param {String} Channel that is currently in Async AGI. - * @param {String} Application to execute. - * @param {String} This will be sent back in CommandID header of AsyncAGI exec event notification. - * @augments Action - */ -function AGI(channel, command, commandId) { - AGI.super_.call(this, 'AGI'); - this.set('Channel', channel); - this.set('Command', command); - this.set('CommandID', commandId); -} - -/** - * BlindTransfer Action. - * @constructor - * @see Action(String) - * @see https://wiki.asterisk.org/wiki/display/AST/Asterisk+12+ManagerAction_BlindTransfer - * @param {String} Source channel that wants to transfer the target channel. - * @param {String} Context to transfer the target channel to. - * @param {String} Extension inside the context to transfer the target channel to. - * @augments Action - */ -function BlindTransfer(channel, context, extension) { - BlindTransfer.super_.call(this, 'BlindTransfer'); - this.set('Channel', channel); - this.set('Context', context); - this.set('Exten', extension); -} - -/** - * Filter Action. - * @constructor - * @param {String} operation. The value of the "Operation" key. - * @param {String} filter. The value of the "Filter" key. - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/Asterisk+11+ManagerAction_Filter. - * @augments Action - */ -function Filter(operation, filter) { - Filter.super_.call(this, 'Filter'); - this.set('Operation', operation); - this.set('Filter', filter); -} - -/** - * UserEvent Action. - * @constructor - * @param {String} UserEvent. The name of the event. - * @see Action(String) - * @see See https://wiki.asterisk.org/wiki/display/AST/Asterisk+11+ManagerAction_UserEvent. - * @augments Action - */ -function UserEvent(event) { - UserEvent.super_.call(this, 'UserEvent'); - this.set('UserEvent', event); -} - -/** - * - * @param mask - * @constructor - * @see See https://wiki.asterisk.org/wiki/display/AST/Asterisk+11+ManagerAction_Events - */ -function Events(mask) { - Events.super_.call(this, 'Events'); - this.set('Eventmask', mask); -} - -// Inheritance for this module -util.inherits(Action, message.Message); -(function() { - var i; - var actions = [ - Login, - Logoff, - Ping, - Hangup, - CoreShowChannels, - CoreStatus, - CoreSettings, - Status, - DahdiShowChannels, - ListCommands, - AbsoluteTimeout, - SipShowPeer, - SipShowRegistry, - SipQualifyPeer, - SipPeers, - AgentLogoff, - Agents, - AttendedTransfer, - ChangeMonitor, - Command, - CreateConfig, - DahdiDialOffHook, - DahdiDndOff, - DahdiDndOn, - DahdiHangup, - DahdiRestart, - DbDel, - DbDeltree, - DbGet, - DbPut, - ExtensionState, - GetConfig, - GetConfigJson, - GetVar, - SetVar, - JabberSend, - PJSIPNotify, - ListCategories, - PauseMonitor, - LocalOptimizeAway, - Reload, - PlayDtmf, - Park, - ParkedCalls, - Parkinglots, - Monitor, - ModuleCheck, - ModuleLoad, - ModuleUnload, - ModuleReload, - MailboxCount, - MailboxStatus, - VoicemailUsersList, - Originate, - Redirect, - Bridge, - UnpauseMonitor, - StopMonitor, - ShowDialPlan, - SendText, - Queues, - QueueUnpause, - QueuePause, - QueueReset, - QueueSummary, - QueueStatus, - QueueRemove, - QueueRule, - QueueAdd, - QueueLog, - QueueReload, - MeetmeList, - MeetmeMute, - MeetmeUnmute, - ConfbridgeListRooms, - ConfbridgeList, - ConfbridgeKick, - ConfbridgeLock, - ConfbridgeUnlock, - ConfbridgeMute, - ConfbridgeUnmute, - AGI, - BlindTransfer, - Filter, - Events, - UserEvent - ]; - for (i in actions) { - util.inherits(actions[i], Action); - exports[actions[i].name] = actions[i]; + * Helper function to create action classes dynamically. + * @param {String} name - The name of the action. + * @param {Array} params - The parameters for the action. + * @param {Object} additionalProperties - Additional properties for the action. + * @returns {Class} - The generated action class. + */ +const createActionClass = (name, params = [], additionalProperties = {}) => { + return class extends Action { + constructor(...args) { + super(name); + params.forEach((param, index) => { + if (args[index] !== undefined) { + this.set(param, args[index]); + } + }); + Object.keys(additionalProperties).forEach(key => { + this.set(key, additionalProperties[key]); + }); } -})(); - - + }; +}; + +const actionsConfig = [ + { name: 'Login', params: ['Username', 'Secret'] }, + { name: 'CoreShowChannels' }, + { name: 'Ping' }, + { name: 'Hangup' }, + { name: 'CoreStatus' }, + { name: 'Status' }, + { name: 'DahdiShowChannels' }, + { name: 'CoreSettings' }, + { name: 'ListCommands' }, + { name: 'Logoff' }, + { name: 'AbsoluteTimeout' }, + { name: 'SIPShowPeer' }, + { name: 'ExtensionStateList' }, + { name: 'SIPShowRegistry' }, + { name: 'SIPQualifyPeer' }, + { name: 'SIPPeers' }, + { name: 'AgentLogoff' }, + { name: 'Agents' }, + { name: 'AttendedTransfer', params: ['Channel', 'Exten', 'Context', 'Priority'] }, + { name: 'ChangeMonitor', params: ['Channel', 'File'] }, + { name: 'Command', params: ['Command'] }, + { name: 'CreateConfig', params: ['Filename'] }, + { name: 'DahdiDialOffHook', params: ['DAHDIChannel', 'Number'] }, + { name: 'DahdiDndOff', params: ['DAHDIChannel'] }, + { name: 'DahdiDndOn', params: ['DAHDIChannel'] }, + { name: 'DahdiHangup', params: ['DAHDIChannel'] }, + { name: 'DahdiRestart' }, + { name: 'DbDel', params: ['Family', 'Key'] }, + { name: 'DbDeltree', params: ['Family', 'Key'] }, + { name: 'DbGet', params: ['Family', 'Key'] }, + { name: 'DbPut', params: ['Family', 'Key', 'Value'] }, + { name: 'ExtensionState', params: ['Exten', 'Context'] }, + { name: 'GetConfig', params: ['Filename', 'Category'] }, + { name: 'GetConfigJson', params: ['Filename'] }, + { name: 'GetVar', params: ['Variable', 'Channel'] }, + { name: 'JabberSend', params: ['Jabber', 'JID', 'Message'] }, + { name: 'ListCategories', params: ['Filename'] }, + { name: 'PauseMonitor', params: ['Channel'] }, + { name: 'UnpauseMonitor', params: ['Channel'] }, + { name: 'StopMonitor', params: ['Channel'] }, + { name: 'LocalOptimizeAway', params: ['Channel'] }, + { name: 'SetVar', params: ['Variable', 'Value', 'Channel'] }, + { name: 'Reload', params: ['Module'] }, + { name: 'PlayDtmf', params: ['Channel', 'Digit'] }, + { name: 'Park', params: ['Channel', 'Channel2', 'Timeout', 'Parkinglot'] }, + { name: 'ParkedCalls', params: ['ParkingLot'] }, + { name: 'Parkinglots' }, + { name: 'Monitor', params: ['Channel', 'Filename'], additionalProperties: { format: 'wav', mix: 'true' } }, + { name: 'ModuleCheck', params: ['Module'] }, + { name: 'ModuleLoad', params: ['Module'], additionalProperties: { LoadType: 'load' } }, + { name: 'ModuleUnload', params: ['Module'], additionalProperties: { LoadType: 'unload' } }, + { name: 'ModuleReload', params: ['Module'], additionalProperties: { LoadType: 'reload' } }, + { name: 'MailboxCount', params: ['Mailbox'] }, + { name: 'MailboxStatus', params: ['Mailbox'] }, + { name: 'VoicemailUsersList' }, + { name: 'Originate', params: ['Channel', 'Exten', 'Priority', 'Application', 'Data', 'Timeout', 'CallerID', 'Account', 'Async', 'Codecs'] }, + { name: 'Redirect', params: ['Channel', 'Exten', 'Context', 'Priority', 'ExtraChannel', 'ExtraExten', 'ExtraContext', 'ExtraPriority'] }, + { name: 'Bridge', params: ['Channel1', 'Channel2', 'Tone'] }, + { name: 'ShowDialPlan', params: ['Context', 'Extension'] }, + { name: 'SendText', params: ['Channel', 'Message'] }, + { name: 'Queues' }, + { name: 'QueueReload', params: ['queue', 'members', 'rules', 'parameters'] }, + { name: 'QueueUnpause', params: ['Interface', 'Queue', 'Reason'], additionalProperties: { paused: 'false' } }, + { name: 'QueuePause', params: ['Interface', 'Queue', 'Reason'], additionalProperties: { paused: 'true' } }, + { name: 'QueueSummary', params: ['Queue'] }, + { name: 'QueueRule', params: ['Rule'] }, + { name: 'QueueStatus', params: ['Queue', 'Member'] }, + { name: 'QueueReset', params: ['Queue'] }, + { name: 'QueueRemove', params: ['Interface', 'Queue'] }, + { name: 'QueueAdd', params: ['Interface', 'Queue', 'Paused', 'MemberName', 'Penalty'] }, + { name: 'QueueLog', params: ['Queue', 'Event', 'Message', 'Interface', 'UniqueId'] }, + { name: 'MeetmeList', params: ['Conference'] }, + { name: 'MeetmeMute', params: ['Meetme', 'Usernum'] }, + { name: 'MeetmeUnmute', params: ['Meetme', 'Usernum'] }, + { name: 'ConfbridgeListRooms' }, + { name: 'ConfbridgeList', params: ['Conference'] }, + { name: 'ConfbridgeKick', params: ['Conference', 'Channel'] }, + { name: 'ConfbridgeLock', params: ['Conference'] }, + { name: 'ConfbridgeUnlock', params: ['Conference'] }, + { name: 'ConfbridgeMute', params: ['Conference', 'Channel'] }, + { name: 'ConfbridgeUnmute', params: ['Conference', 'Channel'] }, + { name: 'AGI', params: ['Channel', 'Command', 'CommandID'] }, + { name: 'BlindTransfer', params: ['Channel', 'Context', 'Extension'] }, + { name: 'Filter', params: ['Operation', 'Filter'] }, + { name: 'UserEvent', params: ['UserEvent'] }, + { name: 'Events', params: ['Eventmask'] }, +]; + +const exportedActions = {}; + +actionsConfig.forEach(({ name, params, additionalProperties }) => { + exportedActions[name] = createActionClass(name, params, additionalProperties); +}); + +module.exports = exportedActions; diff --git a/src/message/event.js b/src/message/event.js index 005b5b7..6f00f3e 100644 --- a/src/message/event.js +++ b/src/message/event.js @@ -16,29 +16,28 @@ * limitations under the License. * */ + /** * @fileoverview Base event class. - * - * @author Marcelo Gornstein - http://marcelog.github.com - * Website: http://marcelog.github.com/Nami */ -message = require(__dirname + '/message.js'); -util = require('util'); + +const { Message } = require(__dirname + '/message.js'); /** - * Base event class. Every async event from the server ends up being an Event() - * @constructor - * @param {String} data A message received from AMI. The End-Of-Message indicator - * has to be already stripped out. This will call unserialize() to build the actual - * Message object. - * @see Message#unmarshall(String) - * @augments Message + * Base event class. Every async event from the server ends up being an Event. + * @extends Message */ -function Event(data) { - Event.super_.call(this); - this.unmarshall(data); +class Event extends Message { + /** + * @constructor + * @param {String} data A message received from AMI. The End-Of-Message indicator + * has to be already stripped out. This will call unserialize() to build the actual + * Message object. + */ + constructor(data) { + super(); + this.unmarshall(data); + } } -util.inherits(Event, message.Message); -exports.Event = Event; - +module.exports = { Event }; diff --git a/src/message/message.js b/src/message/message.js index 8e318ac..26fcea7 100644 --- a/src/message/message.js +++ b/src/message/message.js @@ -16,102 +16,49 @@ * limitations under the License. * */ -/** - * @fileoverview Base message class. - * - * @author Marcelo Gornstein - http://marcelog.github.com - * Website: http://marcelog.github.com/Nami - */ - -/** - * Base message class. - * @constructor - */ -function Message() { - this.lines = []; - this.EOL = "\r\n"; - this.variables = {}; -} +class Message { + constructor() { + this.lines = []; + this.EOL = "\r\n"; + this.variables = {}; + } -/** - * Used to serialize this message to a text representation understood by - * AMI. Will return a set of lines delimited by \r\n and the message is delimited by - * \r\n\r\n. - * @returns {String} - */ -Message.prototype.marshall = function () { - var output = "", key; - for (key in this) { - if (key === 'variables') { - continue; - } - if (this.hasOwnProperty(key)) { - if (key !== 'lines' && key !== 'EOL' && (typeof (this[key]) !== 'function')) { - output = output + key + ": " + this[key] + this.EOL; + marshall() { + let output = ""; + for (const [key, value] of Object.entries(this)) { + if (key !== 'variables' && key !== 'lines' && key !== 'EOL' && typeof value !== 'function') { + output += `${key}: ${value}${this.EOL}`; } } + for (const [key, value] of Object.entries(this.variables)) { + output += `Variable: ${key}=${value}${this.EOL}`; + } + return output + this.EOL; } - for (key in this.variables) { - output = output + 'Variable: ' + key + '=' + this.variables[key] + this.EOL; - } - output = output + this.EOL; - return output; -}; -/** - * Used to unserialize this message given a text representation understood by AMI. - * Will split each line by \r\n and extract "key: value" pairs. Each key will be set - * as a property inside this Message object with the corresponding value. - * @returns void - */ -Message.prototype.unmarshall = function (data) { - var value, parts, key, line = 0; - this.lines = data.split(this.EOL); - for (; line < this.lines.length; line = line + 1) { - parts = this.lines[line].split(":"); - key = parts.shift(); - /* - * This is so, because if this message is a response, specifically a response to - * something like "ListCommands", the value of the keys, can contain the semicolon - * ":", which happens to be token to be used to split keys and values. AMI does not - * specify anything like an escape character, so we cant distinguish wether we're - * dealing with a multi semicolon line or a standard key/value line. - */ - if (parts.length > 1) { - value = parts.join(':'); - } else if (parts.length === 1) { - value = parts[0]; - } - var keySafe = key.replace(/-/, '_').toLowerCase(); - var valueSafe = value.replace(/^\s+/g, '').replace(/\s+$/g, ''); - /* - * SetVar contains Variable: header, but value should not include '=' in this case - */ - if (keySafe.match(/variable/) !== null && valueSafe.match(/=/) !== null) { - var variable = valueSafe.split("="); - this.variables[variable[0]] = variable[1]; - } else { - this.set(keySafe, valueSafe); + unmarshall(data) { + this.lines = data.split(this.EOL); + for (const line of this.lines) { + const parts = line.split(":"); + const key = parts.shift().replace(/-/, '_').toLowerCase(); + const value = parts.join(':').trim(); + if (key.includes('variable') && value.includes('=')) { + const [varKey, varValue] = value.split("="); + this.variables[varKey.trim()] = varValue.trim(); + } else { + this.set(key, value); + } } } -}; -/** - * Call this one to set a property into this message. - * @param {String} name The name of the property. - * @param {String} value The value for the property. - * @returns void - */ -Message.prototype.set = function (name, value) { - this[name] = value; -}; + set(name, value) { + this[name] = value; + } -/** - * Returns the value for the given Message property. - * @returns {String} - */ -Message.prototype.get = function (name) { - return this[name]; -}; + get(name) { + return this[name]; + } +} + +module.exports = { Message }; -exports.Message = Message; diff --git a/src/message/response.js b/src/message/response.js index 98cccb8..48dcc4d 100644 --- a/src/message/response.js +++ b/src/message/response.js @@ -18,30 +18,27 @@ */ /** * @fileoverview Base response class. - * - * @author Marcelo Gornstein - http://marcelog.github.com - * Website: http://marcelog.github.com/Nami */ -message = require(__dirname + '/message.js'); -util = require('util'); + +const { Message } = require(__dirname + '/message.js'); /** * Base response class. - * @constructor - * @param {String} data A message received from AMI. The End-Of-Message indicator - * has to be already stripped out. This will call unserialize() to build the actual - * Message object. - * @see Message(String) - * @see Message#unmarshall(String) - * @augments Message - * @property {Event[]} events Events related to this response (can be empty). + * @extends Message */ -function Response(data) { - Response.super_.call(this); - this.unmarshall(data); - this.events = []; +class Response extends Message { + /** + * @constructor + * @param {String} data A message received from AMI. The End-Of-Message indicator + * has to be already stripped out. This will call unserialize() to build the actual + * Message object. + * @property {Event[]} events Events related to this response (can be empty). + */ + constructor(data) { + super(); + this.unmarshall(data); + this.events = []; + } } -util.inherits(Response, message.Message); -exports.Response = Response; - +module.exports = { Response }; diff --git a/src/nami.js b/src/nami.js index 098d7a1..662de5a 100644 --- a/src/nami.js +++ b/src/nami.js @@ -16,21 +16,22 @@ * limitations under the License. * */ +/*! + * Nami core class. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + */ /** * @fileoverview Nami client code. - * - * @author Marcelo Gornstein - http://marcelog.github.com - * Website: http://marcelog.github.com/Nami */ -var net = require('net'); -var events = require('events'); -var action = require(__dirname + '/message/action.js'); -var namiResponse = require(__dirname + '/message/response.js'); -var util = require('util'); -var namiEvents = require(__dirname + '/message/event.js'); -var timer = require('timers'); +const net = require('net'); +const events = require('events'); +const action = require(__dirname + '/message/action.js'); +const namiResponse = require(__dirname + '/message/response.js'); +const util = require('util'); +const namiEvents = require(__dirname + '/message/event.js'); /** * Nami client. @@ -39,60 +40,41 @@ var timer = require('timers'); * @augments EventEmitter */ function Nami(amiData) { - var self = this; Nami.super_.call(this); this.logLevel = 3; // debug level by default. - - var genericLog = function(minLevel, fun, msg) { - if(self.logLevel >= minLevel) { - fun(msg); - } - }; - this.logger = amiData.logger || { - error: function(msg) { genericLog(0, console.error, msg)}, - warn: function(msg) { genericLog(1, console.warn, msg)}, - info: function(msg) { genericLog(2, console.info, msg)}, - debug: function(msg) { genericLog(3, console.log, msg)} - }; + this.logger = amiData.logger || this.createLogger(); this.connected = false; this.amiData = amiData; this.EOL = "\r\n"; this.EOM = this.EOL + this.EOL; this.welcomeMessage = "Asterisk Call Manager/.*" + this.EOL; - this.received = false; - this.responses = { }; - this.callbacks = { }; - this.on('namiRawMessage', this.onRawMessage); - this.on('namiRawResponse', this.onRawResponse); - this.on('namiRawEvent', this.onRawEvent); + this.received = ""; + this.responses = {}; + this.callbacks = {}; + + this.on('namiRawMessage', this.onRawMessage.bind(this)); + this.on('namiRawResponse', this.onRawResponse.bind(this)); + this.on('namiRawEvent', this.onRawEvent.bind(this)); } -// Nami inherits from the EventEmitter so Nami itself can throw events. util.inherits(Nami, events.EventEmitter); -/** - * Called when a message arrives and is decoded as an event (namiRawEvent event). - * This will actually instantiate an Event. If the event has an ActionID, - * the corresponding response is looked up and will have this event appended. - * Otherwise, the event "namiEvent" is fired. Also, the event "namiEvent" - * is fired (i.e: on event Dial, namiEventDial will be fired). - * - * @see Nami#onRawMessage(String) - * @param {Event} response An Event message. - * @returns void - */ +Nami.prototype.createLogger = function () { + const genericLog = (minLevel, fun, msg) => { + if (this.logLevel >= minLevel) fun(msg); + }; + return { + error: msg => genericLog(0, console.error, msg), + warn: msg => genericLog(1, console.warn, msg), + info: msg => genericLog(2, console.info, msg), + debug: msg => genericLog(3, console.log, msg) + }; +}; + Nami.prototype.onRawEvent = function (event) { this.logger.debug('Got event: ' + util.inspect(event)); - if ( - typeof (event.actionid) !== 'undefined' - && typeof (this.responses[event.actionid]) !== 'undefined' - && typeof (this.callbacks[event.actionid]) !== 'undefined' - ) { + if (event.actionid && this.responses[event.actionid] && this.callbacks[event.actionid]) { this.responses[event.actionid].events.push(event); - if ( - event.event.indexOf('Complete') !== -1 - || ((typeof (event.eventlist) !== 'undefined') && event.eventlist.indexOf('Complete') !== -1) - || event.event.indexOf('DBGetResponse') !== -1 - ) { + if (event.event.includes('Complete') || (event.eventlist && event.eventlist.includes('Complete')) || event.event.includes('DBGetResponse')) { this.callbacks[event.actionid](this.responses[event.actionid]); delete this.callbacks[event.actionid]; delete this.responses[event.actionid]; @@ -103,82 +85,47 @@ Nami.prototype.onRawEvent = function (event) { } }; -/** - * Called when a message arrives and is decoded as a response (namiRawResponse event). - * This will actually instantiate a Response. If this response has associated events - * (still to be received), it is buffered. - * Otherwise, the callback used in send() will be called with the response. - * @see Nami#onRawMessage(String) - * @see Nami#send(String) - * @param {Response} response A Response message. - * @returns void - */ Nami.prototype.onRawResponse = function (response) { this.logger.debug('Got response: ' + util.inspect(response)); - if ( - (typeof (response.message) !== 'undefined') - && (response.message.indexOf('follow') !== -1) - ) { + if (response.message && response.message.includes('follow')) { this.responses[response.actionid] = response; - } else if (typeof (this.callbacks[response.actionid]) !== 'undefined') { + } else if (this.callbacks[response.actionid]) { this.callbacks[response.actionid](response); delete this.callbacks[response.actionid]; delete this.responses[response.actionid]; } }; -/** - * Called by onData() whenever a raw message has been read. - * Will fire "namiRawEvent" if the raw message represents an event. - * Will fire "namiRawResponse" if the raw message represents a response. - * @see Nami#onData(String) - * @param {String} buffer The raw message read from server. - * @returns void - */ Nami.prototype.onRawMessage = function (buffer) { - var response, event; this.logger.debug('Building raw message: ' + util.inspect(buffer)); - if (buffer.match(/^Event: /) !== null) { - event = new namiEvents.Event(buffer); + if (buffer.match(/^Event: /)) { + const event = new namiEvents.Event(buffer); this.emit('namiRawEvent', event); - } else if (buffer.match(/^Response: /) !== null) { - response = new namiResponse.Response(buffer); + } else if (buffer.match(/^Response: /)) { + const response = new namiResponse.Response(buffer); this.emit('namiRawResponse', response); } else { this.logger.warn("Discarded: |" + buffer + "|"); } }; -/** - * Called by node whenever data is available to be read from AMI. - * Will fire "namiRawMessage" for every complete message read. - * @param {String} data The data read from server. - * @see Nami#onRawMessage(String) - * @returns void - */ Nami.prototype.onData = function (data) { - var theEOM = -1, msg; this.logger.debug('Got data: ' + util.inspect(data)); - this.received = this.received.concat(data); - theEOM = -1; + this.received += data; + let theEOM; while ((theEOM = this.received.indexOf(this.EOM)) !== -1) { - msg = this.received.substr(0, theEOM); + const msg = this.received.substring(0, theEOM); this.emit('namiRawMessage', msg); - var startOffset = theEOM + this.EOM.length; - var skippedEolChars = 0; - var nextChar = this.received.substr(startOffset + skippedEolChars, 1); - while (nextChar === "\r" || nextChar === "\n") { + const startOffset = theEOM + this.EOM.length; + let skippedEolChars = 0; + while (["\r", "\n"].includes(this.received[startOffset + skippedEolChars])) { skippedEolChars++; - nextChar = this.received.substr(startOffset + skippedEolChars, 1); - }; + } this.logger.debug('Skipped ' + skippedEolChars + ' bytes'); - this.received = this.received.substr(startOffset + skippedEolChars); + this.received = this.received.substring(startOffset + skippedEolChars); } }; -/** - * Called when the connection is established to AMI. - * @returns void - */ + Nami.prototype.onConnect = function () { this.connected = true; }; @@ -187,141 +134,74 @@ Nami.prototype.onClosed = function () { this.connected = false; }; -/** - * Called when the first line is received from the server. It will check that - * the other peer is a valid AMI server. If not valid, the event "namiInvalidPeer" - * will be fired. If not, a login is tried, and onData() is set as the new handler - * for incoming data. An anonymous function will handle the login response, firing - * "namiLoginIncorrect" if the username/password were not correctly validated. - * On successfull connection, "namiConnected" is emitted. - * @param {String} data The data read from server. - * @see Nami#onData(String) - * @see Login(String, String) - * @returns void - */ Nami.prototype.onWelcomeMessage = function (data) { - var self = this, welcome; this.logger.debug('Got welcome message: ' + util.inspect(data)); - var re = new RegExp(this.welcomeMessage, ""); - if (data.match(re) === null) { + if (!data.match(new RegExp(this.welcomeMessage))) { this.emit('namiInvalidPeer', data); } else { - this.socket.on('data', function (data) { - self.onData(data); - }); - this.send( - new action.Login(this.amiData.username, this.amiData.secret), - function (response) { - if (response.response !== 'Success') { - self.emit('namiLoginIncorrect'); - } else { - self.emit('namiConnected'); - } + this.socket.on('data', this.onData.bind(this)); + this.send(new action.Login(this.amiData.username, this.amiData.secret), response => { + if (response.response !== 'Success') { + this.emit('namiLoginIncorrect'); + } else { + this.emit('namiConnected'); } - ); + }); } }; -/** - * Closes the connection to AMI. - * @returns void - */ + Nami.prototype.close = function () { - var self = this; - this.send(new action.Logoff(), function () { self.logger.info('Logged out'); }); + this.send(new action.Logoff(), () => this.logger.info('Logged out')); this.logger.info('Closing connection'); this.removeAllListeners(); - this.socket.removeAllListeners(); - this.socket.end(); + if (this.socket) { + this.socket.removeAllListeners(); + this.socket.end(); + } this.onClosed(); }; -/** - * Opens the connection to AMI. - * @returns void - */ Nami.prototype.open = function () { this.logger.debug('Opening connection'); this.received = ""; this.initializeSocket(); }; -/** - * Creates a new socket and handles connection events. - * @returns undefined - */ Nami.prototype.initializeSocket = function () { this.logger.debug('Initializing socket'); - var self = this; - if (this.socket && !this.socket.destroyed) { this.socket.removeAllListeners(); this.socket.end(); } - this.socket = new net.Socket(); this.socket.setEncoding('ascii'); - var baseEvent = 'namiConnection'; - - this.socket.on('connect', function() { - self.logger.debug('Socket connected'); - self.onConnect(); - var event = { event: 'Connect' }; - self.emit(baseEvent + event.event, event); - }); - - // @param {Error} error Fires right before the `close` event - this.socket.on('error', function (error) { - self.logger.debug('Socket error: ' + util.inspect(error)); - var event = { event: 'Error', error: error }; - self.emit(baseEvent + event.event, event); - }); - - // @param {Boolean} had_error If the connection closed from an error. - this.socket.on('close', function (had_error) { - self.logger.debug('Socket closed'); - self.onClosed(); - var event = { event: 'Close', had_error: had_error }; - self.emit(baseEvent + event.event, event); - }); - - this.socket.on('timeout', function () { - self.logger.debug('Socket timeout'); - var event = { event: 'Timeout' }; - self.emit(baseEvent + event.event, event); - }); - - this.socket.on('end', function () { - self.logger.debug('Socket ended'); - var event = { event: 'End' }; - self.emit(baseEvent + event.event, event); - }); - - this.socket.once('data', function (data) { - self.onWelcomeMessage(data); - }); + const baseEvent = 'namiConnection'; + this.socket.on('connect', () => this.handleSocketEvent('Connect', baseEvent)); + this.socket.on('error', error => this.handleSocketEvent('Error', baseEvent, error)); + this.socket.on('close', had_error => this.handleSocketEvent('Close', baseEvent, had_error)); + this.socket.on('timeout', () => this.handleSocketEvent('Timeout', baseEvent)); + this.socket.on('end', () => this.handleSocketEvent('End', baseEvent)); + this.socket.once('data', data => this.onWelcomeMessage(data)); this.socket.connect(this.amiData.port, this.amiData.host); }; -/** - * Reopens the socket connection to AMI. - * @returns undefined - */ +Nami.prototype.handleSocketEvent = function (eventType, baseEvent, additionalData) { + this.logger.debug(`Socket ${eventType.toLowerCase()}`); + if (eventType === 'Connect') { + this.onConnect(); + } else if (eventType === 'Close') { + this.onClosed(); + } + this.emit(`${baseEvent}${eventType}`, { event: eventType, ...additionalData && { additionalData } }); +}; + Nami.prototype.reopen = function () { this.logger.debug('Reopening connection'); this.initializeSocket(); }; -/** - * Sends an action to AMI. - * - * @param {Action} action The action to be sent. - * @param {function} callback The optional callback to be invoked when the - * responses arrives. - * - * @returns void - */ Nami.prototype.send = function (action, callback) { this.logger.debug('Sending: ' + util.inspect(action)); this.callbacks[action.ActionID] = callback; @@ -333,4 +213,3 @@ exports.Nami = Nami; exports.Actions = action; exports.Event = namiEvents; exports.Response = namiResponse; -