We force Asterisk to call and connect two external numbers

Remote work is essential these days. Call center operators are also on the go. The challenge arose of providing these operators with the ability to make calls to clients via an office server. But here’s the problem: mobile internet quality doesn’t always allow for softphone operation. From the very first week of testing, we were bombarded with complaints from operators about audio loss, artifacts (echo, lag, etc.), and even complete non-functioning.

The only available solution to this problem was to connect operators to clients via mobile voice, using Asterisk as a transit switch. Some might wonder: why not just call from one phone to another?
Well, because calls have to be recorded, and clients want to see the call center number instead of a bunch of different operator numbers.

The simplest way to do this would be to allow operators to dial the called party’s number after dialing the switchboard. However, manually dialing each client’s number would have caused operators incredible butt-hurt. For this reason, we immediately rejected this option and instead proposed implementing an API that would accept two external numbers as arguments and force Asterisk to dial and connect them.

Next, let’s look at the implementation of this task.

First, let’s add a context that will handle calls initiated by the script.

Open the file /etc/astersk/extensions_custom.conf (relevant for FreePBX) and add the following:

 [commutate]
exten => _X.,1,Dial(SIP/gorod/${EXTEN});
exten => _X.,n,Macro(dialstatus,s,1)
exten => _X.,n,Hangup ;

[macro-dialstatus]
exten => s,1,Answer
exten => s,n,Goto(s-${DIALSTATUS},1)
exten => s-NOANSWER,1,Hangup
exten => s-CONGESTION,1,Congestion
exten => s-CANCEL,1,Hangup
exten => s-BUSY,1,Playtones(425/375,0/375)
exten => s-BUSY,n,Busy(7)
exten => s-BUSY,n,Hangup
exten => s-CHANUNAVAIL,1,Hangup 

Where «city» is the name of the trunk through which the call to the second subscriber will be made.

When working with the PJSIP driver, the second line will look like this:

 exten => _X.,1,Dial(PJSIP/${EXTEN}@gorod); 

Reload the asterisk configuration files using the core reload command in the CLI.

If you’re using FreePBX, you already have the management interface enabled. You can find the details for interacting with it in the /etc/asterisk/manager.conf file.

cat /etc/asterisk/manager.conf

 [general]
enabled = yes
port = 5038
bindaddr = 0.0.0.0
displayconnects=no ;only effects 1.6+

[admin]
secret = supersecretpass
deny=0.0.0.0/0.0.0.0
permit=127.0.0.1/255.255.255.0
read = system,call,log,verbose,command,agent,user,config,command,dtmf,reporting,cdr,dialplan,originate,message
write = system,call,log,verbose,command,agent,user,config,command,dtmf,reporting,cdr,dialplan,originate,message
writetimeout = 5000

#include manager_additional.conf
#include manager_custom.conf 

Here we are mainly interested in the manager’s login and password.

Now, we need to create a PHP script that will be our API. Create a file with any convenient name in the web directory.

vi /var/www/html/call.php

 <pre class="wp-block-syntaxhighlighter-code"><?php
if($_REQUEST['n1'] >= 100000000 && $_REQUEST['n2'] >= 100000000 && $_REQUEST['n1'] < 1000000000 && $_REQUEST['n2'] < 1000000000){
	$n1 = $_REQUEST['n1'];
	$n2 = $_REQUEST['n2'];
	
	$timeout = 10;
	$socket = fsockopen("localhost","5038", $errno, $errstr, $timeout);
	          fputs($socket, "Action: Login\r\n");
              fputs($socket, "UserName: admin\r\n");
              fputs($socket, "Secret: supersecretpass\r\n\r\n");
			  $wrets=fgets($socket,128);
				
	echo $wrets."\r\n";
	
	            fputs($socket, "Action: Originate\r\n" );
                fputs($socket, "Channel: local/".$n1."@from-internal\r\n" );
				fputs($socket, "MaxRetries: 0\r\n" );
				fputs($socket, "WaitTime: 30\r\n" );
                fputs($socket, "Exten: $n2\r\n" );
                fputs($socket, "Context: commutate\r\n" );
                fputs($socket, "Priority: 1\r\n" );
                fputs($socket, "Async: yes\r\n\r\n" );

                $wrets=fgets($socket,128);
                echo $wrets;
	
	
	//echo "TRUE";
} else {
	echo 'FALSE';
}
?></pre> 

Don’t throw slippers at me for my completely idiotic method of checking arguments. In my case, I need to catch nine-digit numbers that can’t possibly start with a zero, and I was too lazy to write a regular expression. ;)) In any other case, you can make the check more kosher and even add an approved list of operator numbers.

We try to call the script using a link like https://[server address]/call.php?n1=XXXXXXXXXX&n2=XXXXXXXXX, after replacing XXXXXXXXX with the operator and subscriber numbers.

If everything is configured correctly, the server will route the call to number n1, and if the connection is successful, number n2 will be dialed. If the second call is answered, the operator on channel n1 will be connected to the subscriber on number n2. Otherwise, the entire session will be terminated by the server.

This script can also be used for the web callback widget by specifying the numbers of operators or call groups in advance as the n1 argument.