Hi guys and thank you for an awesome tool
I have been given the task of integrating FreePBX and Asterisk into our inhouse developed CRM system, and one of the tasks I have been assigned is to track incoming, outgoing, and originated calls. After weeks of “shotgun debugging”, I’ve figured out the smart way to track calls is to use the Asterisk Management API`s “CDR event”, published when a call hangs up. At which point I’ve got access to StartTime, AnswerTime and FinishTime, which is awesome, and allows me to historically store phone calls in my own database.
However, I also need to track when a call is being “Originated”, and I also need to have a callback notification every time a call is created from the server and out, in addition to when an incoming call comes into the server.
After lots of debugging, testing and Googling, I’ve found somebody telling me that I should subscribe to the “NewState” event - Which I am currently doing. My problem though is that when I place a call from for instance an extension to an external number or something similar, either physically using my (soft) phone, or by invoking the “Originate” action, I get tons of NewState events, and I’m supposed to “translate” this into a single invocation to my own system, allowing me to track the following …
- External Call placed (possibly through invoking “Originate”)
- Incoming calls detected
This is necessary since I’ll need to be able to hangup calls and redirect calls from within my own system. Hence, the CDR event is for this purpose “too late” for me …
And since I get tons of these events when a call is places, and some of these events have (to be polite) “weird data associated with them”, I am struggling to say the least how to figure out exactly what is what here …
Below is an example of how it looks like when a call is “Originated”.
NewState EVENT
CallerIdName - 'Outbound Call'
CallerIdNum - '555555555'
Channel - 'SIP/Coperato_1-00000034'
ChannelState - '6'
ChannelStateDesc - 'Up'
ConnectedLineName - 'NameOfOurSoftware'
Connectedlinenum - '<unknown>'
NewState EVENT
CallerIdName - 'NameOfOurSoftware'
CallerIdNum - '<unknown>'
Channel - 'Local/[email protected];2'
ChannelState - '6'
ChannelStateDesc - 'Up'
ConnectedLineName - 'Outbound Call'
Connectedlinenum - '555555555'
NewState EVENT
CallerIdName - 'Outbound Call'
CallerIdNum - '555555555'
Channel - 'Local/[email protected];1'
ChannelState - '6'
ChannelStateDesc - 'Up'
ConnectedLineName - 'NameOfMySoftware'
Connectedlinenum - '<unknown>'
NewState EVENT
CallerIdName - 'Extension 111'
CallerIdNum - '111'
Channel - 'SIP/111-00000035'
ChannelState - '5'
ChannelStateDesc - 'Ringing'
ConnectedLineName - 'Outbound Call'
Connectedlinenum - '555555555'
NewState EVENT
CallerIdName - 'Extension 111'
CallerIdNum - '111'
Channel - 'SIP/111-00000035'
ChannelState - '6'
ChannelStateDesc - 'Up'
ChannelStateDesc - 'Up'
ConnectedLineName - 'Outbound Call'
Connectedlinenum - '555555555'
Below is how it looks like when a call is placed from my soft phone to an external number …
NewState EVENT
CallerIdName - 'Extension 111'
CallerIdNum - '111'
Channel - 'SIP/111-00000038'
ChannelState - '4'
ChannelStateDesc - 'Ring'
ChannelStateDesc - 'Ring'
ConnectedLineName - '<unknown>'
Connectedlinenum - '<unknown>'
NewState EVENT
CallerIdName - 'Outbound Call'
CallerIdNum - '555555555'
Channel - 'SIP/Coperato_1-00000039'
ChannelState - '6'
ChannelStateDesc - 'Up'
ConnectedLineName - '<unknown>'
Connectedlinenum - '999999999'
NewState EVENT
CallerIdName - '<unknown>'
CallerIdNum - '999999999'
Channel - 'SIP/111-00000038'
ChannelState - '6'
ChannelStateDesc - 'Up'
ConnectedLineName - 'Outbound Call'
Connectedlinenum - '555555555'
Then finally how it looks like when an incoming call is received …
NewState EVENT
CallerIdName - '999999999'
CallerIdNum - '999999999'
Channel - 'SIP/Coperato_1-00000036'
ChannelState - '4'
ChannelStateDesc - 'Ring'
ConnectedLineName - '<unknown>'
Connectedlinenum - '<unknown>'
NewState EVENT
CallerIdName - 'Extension 111'
CallerIdNum - '111'
Channel - 'SIP/111-00000037'
ChannelState - '5'
ChannelStateDesc - 'Ringing'
ConnectedLineName - '999999999'
Connectedlinenum - '999999999'
NewState EVENT
CallerIdName - 'Extension 111'
CallerIdNum - '111'
Channel - 'SIP/111-00000037'
ChannelState - '6'
ChannelStateDesc - 'Up'
ConnectedLineName - '999999999'
Connectedlinenum - '999999999'
NewState EVENT
CallerIdName - '999999999'
CallerIdNum - '999999999'
Channel - 'SIP/Coperato_1-00000036'
ChannelState - '6'
ChannelStateDesc - 'Up'
ConnectedLineName - '<unknown>'
Connectedlinenum - '<unknown>'
My problem is figuring out what is what. At the moment I am first checking the ChannelState and State attributes of these events. If it is 4 or “Up”, I check if the CallerIdNum is less in size than 6 digits (I can assume that extensions are always less than 6 digits).
Then if the state is “Up” or the ChannelState is 6, I assume an outbounding call if the CallerIdName is “Outbound Call”. However, at this point I check to see if ConnectedLineName is “NameOfMySoftware”, and if it is, I only invoke my callback if the Channel contains “@dialer”.
If the state is “Ringing” or ChannelState is “5”, and the ConnectedLineName is “Outbound Call”, I invoke my callback as an “Incoming call”.
Basically, the code is a total mess, and I am struggling with it. And although it kind of works, I suspect some of these things, such as the CallerIdName of “Outbound Call” might be configured differently in different configurations of FreePBX …
If anybody here have any advice as to how I can actually track an incoming, outgoing (including “Originated” call), immediately once it occurs, I would deeply appreciate it …
FYI - The actual phone numbers above have been changed to anonymize my data …
Alternatively, if some of you here know some resource as to where I can figure out this information, I would deeply appreciate it. The problems is arguably more about the Asterisk Management API I assume than the FreePBX distro of it. I am using AsterNET and C# (not that it matters) to implement my own system …
For those fluent in that language, you can see my actual code below …
Inside my “NewState” event handler …
/*
* Figuring out what type of event we should raise, if any.
*/
if (e.State == "Ring" || e.ChannelState == "4") {
/*
* Verifying that this actually is an outgoing, at which point the CallerIdNum will be short.
* Notice, this does not work if a client has setup extensions which are 6 or more digits,
* which according to Erez would never happen.
*/
if (e.CallerIdNum != null && e.CallerIdNum.Length >= 6) {
// Raising event.
_callback.OutgoingAsync(dialerResult);
}
} else if (e.State == "Up" || e.ChannelState == "6") {
// Checking that this was an outbound call.
if (e.CallerIdName == "Outbound Call") {
/*
* If this was an "Originated" call, created by the module itself, then the ConnectedLineName
* for the event will be the caller id for the app as it is placing calls, at which point a
* NewState event with the channel created towards the dialer will be published at some point.
*
* To make sure we don't publish the same event twice, we only invoke OutgoingAsync callback
* for the "@dialer" channel, which implies the channel will be the same as the channel
* returned by "Originate".
*/
if (e.ConnectedLineName == _callerIdForOutgoing) {
// This is a call that was "Originated" by the module itself.
if (e.Channel.Contains("@dialer")) {
// Raising event.
_callback.AnsweredAsync(dialerResult);
}
} else {
// This is a call that was NOT "Originated" by the system.
_callback.AnsweredAsync(dialerResult);
}
}
} else if (e.State == "Ringing" || e.ChannelState == "5") {
/*
* Notice, when we "Originate" a call, this piece of code will kick in, but with the
* ConnectedLineName being "Outbound Call", at which point we should (obviously) not
* call our duplex callback.
*/
if (e.ConnectedLineName != "Outbound Call") {
// Raising event.
_callback.IncomingAsync(dialerResult);
}
}