Record During Dial() - Unusual Problem Requires Creative Solution

My current setup: incoming calls are routed to a dialplan that plays a brief greeting and uses the dial() command to ring the appropriate extensions while playing on-hold music.

What I am trying to do: record (monitor to a file) the inbound audio of incoming calls up until the point that those calls are answered. In other words, only record up until the point the dial() command finishes executing and a call is either connected/bridged or sent somewhere else (like voicemail).

Why I am trying to do this: The main reason is that I am receiving automated calls which treat the on-hold music as a human answering (or a voicemail) and start playing back important information during that dialing period. By the time a real human answers the call (or the call actually gets sent to voicemail), the important information has already been missed.

The problem: the monitor() command continues to record the audio after a call has been answered (which is not desirable). There is a StopMonitor() command but if you place it after the dial() command then it will only get executed if the call is not answered (so obviously this does not solve my problem).

Possible Solutions: I have considered using Pre-Bridge Handlers to execute a macro or gosub routine which would then call StopMonitor() when the call is answered.

The issue is that If I call StopMonitor() in pre-bridge macro or gosub routine, it seems to have no effect and the call continues to be recorded, perhaps because—according to the Asterisk docs—“these handlers are executed on the called party channel, after it is answered” even though they are executed before the call is bridged. Unfortunately, the StopMonitor() dialplan command doesn’t appear to allow you to specify a channel to stop monitoring.

After some research, I discovered that the AMI also has a StopMonitor API command that does let you specify the channel, however all my attempts at using the AMI (through PHP AGI) to accomplish this have failed. Any help/advice is appreciated, thanks.

Use the M(x[^arg]) option on the dial command and StopMonitor()
Runs the macro x when the call is answered, optionally passing ^ (caret) separated arguments. The macro may set the MACRO_RESULT channel variable.

Thanks ambiorixg12. However, as I indicated above, I already tried executing a macro this way which calls StopMonitor() and it seems to have no effect (the call continues to be recorded after being answered/bridged).

The line where I call the macro looks something like this:
exten => 300,n,Dial(SIP/110,30,itTm(stdmusic)M(answered))

The macro itself looks something like this:
exten => s,1,StopMonitor()

When I answer, the CLI/log reports:
Executing [s@macro-answered:1] StopMonitor("SIP/110-00000001", "") in new stack

But the call continues to be recorded after that.

I’m not sure if a local channel will optimised out when there is a monitor on it, but you could try a local channel with Mixmonitor.

Thanks David, but I’m not sure how a local channel would work. Can you elaborate on how I would use a local channel to stop the monitor just as a call is being answered/connected? Thanks.

When an optimisable local channel is bridged to its destination, it is collapsed out of existence. It is possible that you can place the monitor at a position that disappears when that happens. However, I cannot guarantee that the system doesn’t take extreme measures to retain the monitor.

Thanks everyone, I managed to solve the problem.

So my initial intuition was right, I was able to solve the issue by using pre-bridge handlers in combination with an AGI script that uses the AMI API to stop the monitor on the original (caller) channel.

The Solution

(in case anyone needs to do this in the future)

:one: The dial() command should look something like this:

exten => 300,n,Dial(SIP/110,30,itTm(stdmusic)M(answered^${CHANNEL}))

:two: The macro should look something like this:

exten => s,1,AGI(stop_monitor.agi,${ARG1})

:three: The stop_monitor.agi script should look something like this:

#!/usr/bin/php -q
$channel = $argv[1];
$socket = fsockopen("localhost","5038", $errno, $errstr, 2);
fputs($socket, "Action: Login\r\n");
fputs($socket, "UserName: <AMI username>\r\n");
fputs($socket, "Secret: <AMI password>\r\n\r\n");

fputs($socket, "Action: StopMonitor\r\n");
fputs($socket, "Channel: $channel\r\n\r\n");
fputs($socket, "Action: Logoff\r\n\r\n");

while (!feof($socket)) { $wrets .= fread($socket, 8192); }

Note: remember to setup an Asterisk Manager Interface (AMI) account beforehand. You can do this through FreePBX (if you have it installed) and through the unembedded FreePBX option on Elastix.

Note 2: the AGI script should go in /var/lib/asterisk/agi-bin or wherever your default AGI path is. Also don’t forget to chmod (set the access permissions for) the script file so that it’s executable (755 should do the trick).

1 Like

The only difference between this and your previous dial plan solution was this. You didn’t pass the channel name as argument to the macro

exten => 300,n,Dial(SIP/110,30,itTm(stdmusic)M(answered))

The solution that you mentioned earlier didn’t involve using an AGI script or the AMI to stop the monitoring though (unless I misunderstood). You just suggested calling the macro and using the StopMonitor() dialplan command in the macro. However, this method doesn’t work and passing the channel as an argument to the macro makes no difference (I tried) so I didn’t include it in the post.

The difference is the AGI script. I knew from the beginning I had to pass the channel to the script, it’s just that my AGI script had errors in it (which weren’t being logged/reported) so that’s why I couldn’t get it to work properly… but even before I posted this thread asking for help I was already passing the channel to the macro. Once I figured out the error in my AGI script, everything started working as desired.

Edit: just to clarify, the StopMonitor() dialplan command does not allow you to pass a channel as an argument (or at least it doesn’t work when I tried to do that and the Asterisk documentation doesn’t say it accepts any arguments). So that’s why I had to use an AGI/AMI solution.

You are right, AMI in this case was the right fit.

And thanks for sharing your solution with community

And thanks anyway for your help. I realize now (especially after looking at your user profile) that I would probably have been better off just posting my php script in the first place (since I’m sure you would have identified the problem quickly). I guess I was just hoping there was a simpler dialpan solution that didn’t involve AGI/PHP/AMI.