asteriskagi
Version:
NodeJS Fast AGI Server
400 lines (387 loc) • 17.4 kB
text/typescript
/**
* Asterisk AGI (FastAGI) Server for Nodejs / Channel
* @module node-asteriskagi
* @license MIT
* @author Corey S. McFadden <cmcfadden@clearlyip.com>
* @copyright This project is not affiliated with, endorsed by, or sponsored by Digium Inc. or Sangoma Technologies Corp, holders of the "Asterisk" trademark, which is used here for identification purposes only.
*/
import * as net from "net";
import events from "events";
export class AGIChannel extends events.EventEmitter {
private _socket!: net.Socket;
public remoteServer: string | false;
public channel?: string = "";
public language?: string = "en";
public uniqueid?: string = "";
public version?: string = "";
public callerid?: string = "";
public calleridname?: string = "";
public callingpres?: string = "";
public callingani2?: string = "";
public callington?: string = "";
public callingtns?: string = "";
public dnid?: string = "";
public rdnis?: string = "";
public context?: string = "";
public extension?: string = "";
public priority?: string = "";
public enhanced?: string = "";
public accountcode?: string = "";
public threadid?: string = "";
public currently?: string | false;
constructor(props: {
socket: net.Socket;
remoteServer: string | false;
channel?: string;
language?: string;
uniqueid?: string;
version?: string;
callerid?: string;
calleridname?: string;
callingpres?: string;
callingani2?: string;
callington?: string;
callingtns?: string;
dnid?: string;
rdnis?: string;
context?: string;
extension?: string;
priority?: string;
enhanced?: string;
accountcode?: string;
threadid?: string;
}) {
super();
this._socket = props.socket;
this.remoteServer = props.remoteServer;
this.channel = props.channel || "";
this.language = props?.language || "en";
this.uniqueid = props?.uniqueid || "";
this.version = props?.version || "";
this.callerid = props?.callerid || "";
this.calleridname = props?.calleridname || "";
this.callingpres = props?.callingpres || "";
this.callingani2 = props?.callingani2 || "";
this.callington = props?.callington || "";
this.callingtns = props?.callingtns || "";
this.dnid = props?.dnid || "";
this.rdnis = props?.rdnis || "";
this.context = props?.context || "";
this.extension = props?.extension || "";
this.priority = props?.priority || "";
this.enhanced = props?.enhanced || "";
this.accountcode = props?.accountcode || "";
this.threadid = props?.threadid || "";
this.currently = false; // Current operation
}
// Commands mapped to Exec
AddQueueMember = (args: string) => this.Exec("AddQueueMember", args);
AgentLogin = (args: string) => this.Exec("AgentLogin", args);
AgentRequest = (args: string) => this.Exec("AgentRequest", args);
AGI = (args: string) => this.Exec("AGI", args);
AlarmReceiver = (args: string) => this.Exec("AlarmReceiver", args);
AMD = (args: string) => this.Exec("AMD", args);
AttendedTransfer = (args: string) => this.Exec("AttendedTransfer", args);
AudioSocket = (args: string) => this.Exec("AudioSocket", args);
Authenticate = (args: string) => this.Exec("Authenticate", args);
Background = (args: string) => this.Exec("Background", args);
BackGround = (args: string) => this.Exec("BackGround", args);
BackgroundDetect = (args: string) => this.Exec("BackgroundDetect", args);
BlindTransfer = (args: string) => this.Exec("BlindTransfer", args);
Bridge = (args: string) => this.Exec("Bridge", args);
BridgeAdd = (args: string) => this.Exec("BridgeAdd", args);
BridgeWait = (args: string) => this.Exec("BridgeWait", args);
Busy = (args: string | undefined) => this.Exec("Busy", args);
CallCompletionCancel = (args: string) => this.Exec("CallCompletionCancel", args);
CallCompletionRequest = (args: string) => this.Exec("CallCompletionRequest", args);
CELGenUserEvent = (args: string) => this.Exec("CELGenUserEvent", args);
ChangeMonitor = (args: string) => this.Exec("ChangeMonitor", args);
ChanIsAvail = (args: string) => this.Exec("ChanIsAvail", args);
ChannelRedirect = (args: string) => this.Exec("ChannelRedirect", args);
ChanSpy = (args: string) => this.Exec("ChanSpy", args);
ClearHash = (args: string) => this.Exec("ClearHash", args);
ConfBridge = (args: string) => this.Exec("ConfBridge", args);
ConfKick = (args: string) => this.Exec("ConfKick", args);
Congestion = (args: string | undefined) => this.Exec("Congestion", args);
ContinueWhile = (args: string) => this.Exec("ContinueWhile", args);
ControlPlayback = (args: string) => this.Exec("ControlPlayback", args);
DAHDIScan = (args: string) => this.Exec("DAHDIScan", args);
DateTime = (args: string) => this.Exec("DateTime", args);
DBdeltree = (args: string) => this.Exec("DBdeltree", args);
DeadAGI = (args: string) => this.Exec("DeadAGI", args);
Dial = (args: string) => this.Exec("Dial", args);
Dictate = (args: string) => this.Exec("Dictate", args);
Directory = (args: string) => this.Exec("Directory", args);
DISA = (args: string) => this.Exec("DISA", args);
DumpChan = (args: string) => this.Exec("DumpChan", args);
EAGI = (args: string) => this.Exec("EAGI", args);
Echo = () => this.Exec("Echo", undefined);
Else = (args: string) => this.Exec("Else", args);
ElseIf = (args: string) => this.Exec("ElseIf", args);
EndIf = () => this.Exec("EndIf", undefined);
EndWhile = () => this.Exec("EndWhile", undefined);
ExecIf = (args: string) => this.Exec("ExecIf", args);
ExecIfTime = (args: string) => this.Exec("ExecIfTime", args);
ExitIf = (args: string) => this.Exec("ExitIf", args);
ExitWhile = () => this.Exec("ExitWhile", undefined);
ExtenSpy = (args: string) => this.Exec("ExtenSpy", args);
ExternalIVR = (args: string) => this.Exec("ExternalIVR", args);
Festival = (args: string) => this.Exec("Festival", args);
FollowMe = (args: string) => this.Exec("FollowMe", args);
ForkCDR = (args: string | undefined) => this.Exec("ForkCDR", args);
Gosub = (args: string) => this.Exec("Gosub", args);
GosubIf = (args: string) => this.Exec("GosubIf", args);
Goto = (args: string) => this.Exec("Goto", args);
GotoIf = (args: string) => this.Exec("GotoIf", args);
GotoIfTime = (args: string) => this.Exec("GotoIfTime", args);
HangupCauseClear = (args: string) => this.Exec("HangupCauseClear", args);
If = (args: string) => this.Exec("If", args);
ImportVar = (args: string) => this.Exec("ImportVar", args);
Incomplete = (args: string) => this.Exec("Incomplete", args);
Log = (args: string) => this.Exec("Log", args);
Macro = (args: string) => this.Exec("Macro", args);
MacroExclusive = (args: string) => this.Exec("MacroExclusive", args);
MacroExit = (args: string) => this.Exec("MacroExit", args);
MacroIf = (args: string) => this.Exec("MacroIf", args);
MessageSend = (args: string) => this.Exec("MessageSend", args);
Milliwatt = (args: string | undefined) => this.Exec("Milliwatt", args);
MinivmAccMess = (args: string) => this.Exec("MinivmAccMess", args);
MinivmDelete = (args: string) => this.Exec("MinivmDelete", args);
MinivmGreet = (args: string) => this.Exec("MinivmGreet", args);
MinivmMWI = (args: string) => this.Exec("MinivmMWI", args);
MinivmNotify = (args: string) => this.Exec("MinivmNotify", args);
MinivmRecord = (args: string) => this.Exec("MinivmRecord", args);
MixMonitor = (args: string) => this.Exec("MixMonitor", args);
Monitor = (args: string) => this.Exec("Monitor", args);
Morsecode = (args: string) => this.Exec("Morsecode", args);
MP3Player = (args: string) => this.Exec("MP3Player", args);
MSet = (args: string) => this.Exec("MSet", args);
MusicOnHold = (args: string | undefined) => this.Exec("MusicOnHold", args);
NoCDR = (args: string) => this.Exec("NoCDR", args);
ODBC_Commit = (args: string) => this.Exec("ODBC_Commit", args);
ODBC_Rollback = (args: string) => this.Exec("ODBC_Rollback", args);
ODBCFinish = (args: string) => this.Exec("ODBCFinish", args);
Originate = (args: string) => this.Exec("Originate", args);
Page = (args: string) => this.Exec("Page", args);
Park = (args: string) => this.Exec("Park", args);
ParkAndAnnounce = (args: string) => this.Exec("ParkAndAnnounce", args);
ParkedCall = (args: string) => this.Exec("ParkedCall", args);
PauseMonitor = (args: string) => this.Exec("PauseMonitor", args);
PauseQueueMember = (args: string) => this.Exec("PauseQueueMember", args);
Pickup = (args: string) => this.Exec("Pickup", args);
PickupChan = (args: string) => this.Exec("PickupChan", args);
PlayTones = (args: string) => this.Exec("PlayTones", args);
PrivacyManager = (args: string) => this.Exec("PrivacyManager", args);
Proceeding = () => this.Exec("Proceeding", undefined);
Progress = () => this.Exec("Progress", undefined);
Queue = (args: string) => this.Exec("Queue", args);
QueueLog = (args: string) => this.Exec("QueueLog", args);
QueueUpdate = (args: string) => this.Exec("QueueUpdate", args);
RaiseException = (args: string) => this.Exec("RaiseException", args);
Read = (args: string) => this.Exec("Read", args);
ReadExten = (args: string) => this.Exec("ReadExten", args);
ReceiveFAX = (args: string) => this.Exec("ReceiveFAX", args);
ReceiveMF = (args: string) => this.Exec("ReceiveMF", args);
ReceiveSF = (args: string) => this.Exec("ReceiveSF", args);
ReceiveText = (args: string) => this.Exec("ReceiveText", args);
Record = (args: string) => this.Exec("Record", args);
Reload = (args: string | undefined) => this.Exec("Reload", args);
RemoveQueueMember = (args: string) => this.Exec("RemoveQueueMember", args);
ResetCDR = (args: string) => this.Exec("ResetCDR", args);
RetryDial = (args: string) => this.Exec("RetryDial", args);
Return = (args: string) => this.Exec("Return", args);
Ringing = (args: string) => this.Exec("Ringing", args);
SayAlpha = (args: string) => this.Exec("SayAlpha", args);
SayAlphaCase = (args: string) => this.Exec("SayAlphaCase", args);
SayDigits = (args: string) => this.Exec("SayDigits", args);
SayMoney = (args: string) => this.Exec("SayMoney", args);
SayNumber = (args: string) => this.Exec("SayNumber", args);
SayOrdinal = (args: string) => this.Exec("SayOrdinal", args);
SayPhonetic = (args: string) => this.Exec("SayPhonetic", args);
SayUnixTime = (args: string) => this.Exec("SayUnixTime", args);
SendDTMF = (args: string) => this.Exec("SendDTMF", args);
SendFAX = (args: string) => this.Exec("SendFAX", args);
SendMF = (args: string) => this.Exec("SendMF", args);
SendSF = (args: string) => this.Exec("SendSF", args);
SendText = (args: string) => this.Exec("SendText", args);
Set = (args: string) => this.Exec("Set", args);
SetAMAFlags = (args: string) => this.Exec("SetAMAFlags", args);
SMS = (args: string) => this.Exec("SMS", args);
SoftHangup = (args: string) => this.Exec("SoftHangup", args);
SpeechActivateGrammar = (args: string) => this.Exec("SpeechActivateGrammar", args);
SpeechBackground = (args: string) => this.Exec("SpeechBackground", args);
SpeechCreate = (args: string) => this.Exec("SpeechCreate", args);
SpeechDeactivateGrammar = (args: string) => this.Exec("SpeechDeactivateGrammar", args);
SpeechDestroy = (args: string) => this.Exec("SpeechDestroy", args);
SpeechLoadGrammar = (args: string) => this.Exec("SpeechLoadGrammar", args);
SpeechProcessingSound = (args: string) => this.Exec("SpeechProcessingSound", args);
SpeechStart = (args: string) => this.Exec("SpeechStart", args);
SpeechUnloadGrammar = (args: string) => this.Exec("SpeechUnloadGrammar", args);
StackPop = (args: string) => this.Exec("StackPop", args);
StartMusicOnHold = (args: string) => this.Exec("StartMusicOnHold", args);
Stasis = (args: string) => this.Exec("Stasis", args);
StopMixMonitor = (args: string | undefined) => this.Exec("StopMixMonitor", args);
StopMonitor = (args: string | undefined) => this.Exec("StopMonitor", args);
StopMusicOnHold = () => this.Exec("StopMusicOnHold", undefined);
StopPlayTones = () => this.Exec("StopPlayTones", undefined);
StoreDTMF = (args: string) => this.Exec("StoreDTMF", args);
StreamEcho = (args: string | undefined) => this.Exec("StreamEcho", args);
System = (args: string) => this.Exec("System", args);
TestClient = (args: string) => this.Exec("TestClient", args);
TestServer = (args: string) => this.Exec("TestServer", args);
ToneScan = (args: string | undefined) => this.Exec("ToneScan", args);
Transfer = (args: string) => this.Exec("Transfer", args);
TryExec = (args: string) => this.Exec("TryExec", args);
TrySystem = (args: string) => this.Exec("TrySystem", args);
UnpauseMonitor = () => this.Exec("UnpauseMonitor", undefined);
UnpauseQueueMember = (args: string) => this.Exec("UnpauseQueueMember", args);
UserEvent = (args: string) => this.Exec("UserEvent", args);
VMAuthenticate = (args: string) => this.Exec("VMAuthenticate", args);
VMSayName = (args: string) => this.Exec("VMSayName", args);
VoiceMail = (args: string) => this.Exec("VoiceMail", args);
VoiceMailMain = (args: string | undefined) => this.Exec("VoiceMailMain", args);
VoiceMailPlayMsg = (args: string) => this.Exec("VoiceMailPlayMsg", args);
Wait = (args: string) => this.Exec("Wait", args);
WaitDigit = (args: string | undefined) => this.Exec("WaitDigit", args);
WaitExten = (args: string | undefined) => this.Exec("WaitExten", args);
WaitForCondition = (args: string) => this.Exec("WaitForCondition", args);
WaitForNoise = (args: string | undefined) => this.Exec("WaitForNoise", args);
WaitForRing = (args: string | undefined) => this.Exec("WaitForRing", args);
WaitForSilence = (args: string | undefined) => this.Exec("WaitForSilence", args);
WaitForTone = (args: string | undefined) => this.Exec("WaitForTone", args);
WaitUntil = (args: string | undefined) => this.Exec("WaitUntil", args);
While = (args: string) => this.Exec("While", args);
Zapateller = (args: string) => this.Exec("Zapateller", args);
/**
* Answer Channel
*/
async Answer() {
return await this.send("ANSWER");
}
/**
* Wildcard command handler
* @param {string} command
* @param {string|undefined} args
*/
async Exec(command: string, args: string | undefined) {
try {
this.currently = command;
await this.send(`EXEC ${command} ${args}`);
this.currently = false;
return;
} catch (err) {
return false;
}
}
/**
* Get AGI Variable
* @param {string} variable
* @returns
*/
async getVariable(variable: string) {
try {
return await this.send("GET VARIABLE " + variable);
} catch (err) {
this.emit("error", "getVariable ERROR: " + err);
return false;
}
}
/**
* Hang up channel
*/
async Hangup() {
await this.send("HANGUP");
}
/**
* Do Nothing (No Operation)
* @param {string} args - text
*/
async NoOp(args: string) {
return await this.send(`NOOP ${args}`);
}
/**
* Sends audio file on channel.
* @param {string} filename
*/
async Playback(filename: string) {
try {
this.currently = "Playback";
await this.send("STREAM FILE " + filename + ' ""');
this.currently = false;
return;
} catch (err) {
this.emit("error", "Playback ERROR: " + err);
return false;
}
}
/**
* Send FastAGI command to specified socket
* @param {string} command
*/
async send(command: string) {
return new Promise((resolve, reject) => {
try {
this._socket.once("data", (data) => {
const response: any = this._parseResponse(data.toString().trim());
if (response.code == "520") {
this.emit("error", response.data);
resolve(false);
return;
}
if (response.result < 0) {
this.emit("error", "Dead channel detected.");
this._socket && this._socket.end();
resolve(false);
}
const match = response.data.match(/\((.*?)\)/);
resolve(match ? match[1] : response.data || response.result);
});
if (this._socket.writable) {
this._socket.write(command + "\n", "utf8");
} else {
this.emit("error", "Dead channel detected.");
this._socket && this._socket.end();
resolve(false);
}
} catch (err) {
this.emit("error", "AGI Send Error:" + err);
resolve(false);
return;
}
});
}
/**
* Verbose log line
* @param args - Text to log
*/
async Verbose(args: string) {
return await this.send(`VERBOSE ${args}`);
}
/**
* Parse AGI response codes
* @param {string} str
* @returns object - Response code,result,data
*/
_parseResponse(str: string) {
try {
let match = str.match(/(\d+)\s+result=(-?\d+)(?:\s+(.*))?/);
if (match) {
return {
code: match[1],
result: Number(match[2]) || 0,
data: match[3] || "",
};
}
match = str.match(/(\d+)-(.*)/);
return match
? {
code: match[1],
result: 0,
data: match[2],
}
: { code: "", result: "", data: str };
} catch (err) {
return { code: "", result: "", data: str };
}
}
}