📜 ⬆️ ⬇️

Asterisk. Integration with amoCRM, step-by-step guide

In the network, you can find instructions of different degrees of prescription and completeness of the information presented on the topic in the headline of the article, but even gathering them all together will require straight hands, a file and some patience to achieve the desired catharsis.



Here I will present my experience of connecting Asterisk to amoCRM in the form of step-by-step instructions, highlighting all the necessary nuances, ranging from obtaining an ssl certificate, setting up a web server and ending with a demonstration of the resulting bundle.

* for the impatient, what will happen as a result of the executed manipulations see in
end of article

Introductory


On our test bench installed:
')

Get ssl certificate


In this guide, we will use a free certificate from Let's Encrypt.

Initially, I planned to use StartSSL and wrote step-by-step instructions for obtaining certificates there, but only after I noticed that no browser accepts their root certificates.

The procedure for obtaining it is quite trivial, but I still describe it in steps.

  1. Go to the website letsencrypt.org and click "Get Started"

    screen


  2. Next we are interested in the section With Shell Access, in which we will find all the necessary instructions

    screen

  3. Go to certbot.eff.org and select our software.

    screen

  4. Then follow the instructions and execute

    several teams in kosnoli
     echo "deb http://ftp.debian.org/debian jessie-backports main" >> /etc/apt/sources.list apt-get update apt-get install certbot -t jessie-backports 

  5. Then you need to send a request for a certificate using the certbot utility.
    I went on the most primitive way:

    drove a team

     certbot certonly 

    and followed the steps of the wizard, where he indicated his email, the path to the webroot, the domain name, etc.
    screenshots





  6. At the exit we see

    cherished
     IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at /etc/letsencrypt/live/asterisk.vistep.ru/fullchain.pem. Your cert will expire on 2017-06-27. To obtain a new or tweaked version of this certificate in the future, simply run certbot again. To non-interactively renew *all* of your certificates, run "certbot renew" - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le 
  7. Copy certificates to their locations

    Hidden text
     cp /etc/letsencrypt/live/asterisk.vistep.ru/privkey.pem /etc/nginx/certs/vistep.ru.key cp /etc/letsencrypt/live/asterisk.vistep.ru/fullchain.pem /etc/nginx/certs/vistep.ru.pem 

As rightly noted in the comments, the lifetime of the received certificates is 3 months and they will need to be updated. Take this into account!

Web server setup


As stated in the introductory, we will use the NGINX web server.

I will not begin to breed hollywar'ov and somehow motivate my choice, simply - we have NGINX and we will customize it.

The basis of the config was the remarkable article by DimaSmirnov “Nginx and https. We get class A + ” , for which he, taking the opportunity, express my gratitude.

So, the configuration file of the web server looks like this (some comments are given directly in the config):

/etc/nginx/conf.d/asterisk.vistep.ru.conf
 server { server_name asterisk.vistep.ru; listen 138.201.164.52:80; rewrite ^ https://asterisk.vistep.ru$request_uri? permanent; } server { access_log /var/log/nginx/asterisk.vistep.ru.access.log; error_log /var/log/nginx/asterisk.vistep.ru.error.log; listen 443 ssl; server_name asterisk.vistep.ru; resolver 8.8.8.8; ssl_stapling on; ssl on; ssl_certificate /etc/nginx/certs/vistep.ru.pem; ssl_certificate_key /etc/nginx/certs/vistep.ru.key; ssl_dhparam /etc/nginx/certs/dhparam.pem; ssl_session_timeout 24h; ssl_session_cache shared:SSL:2m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers kEECDH+AES128:kEECDH:kEDH:-3DES:kRSA+AES128:kEDH+3DES:DES-CBC3-SHA:!RC4:!aNULL:!eNULL:!MD5:!EXPORT:!LOW:!SEED:!CAMELLIA:!IDEA:!PSK:!SRP:!SSLv2; ssl_prefer_server_ciphers on; add_header Strict-Transport-Security "max-age=31536000;"; add_header Content-Security-Policy-Report-Only "default-src https:; script-src https: 'unsafe-eval' 'unsafe-inline'; style-src https: 'unsafe-inline'; img-src https: data:; font-src https: data:; report-uri /csp-report"; root /var/www/asterisk; index index.php index.html index.htm index.nginx-debian.html; location records/ { autoindex off; allow 89.108.120.223; allow 89.108.122.9; allow 95.213.171.78; allow 95.213.156.46; allow 209.160.27.20; allow 89.189.163.20; #   -  amoCRM   ,   -  ,       ;)    - https://www.amocrm.ru/security/iplist.txt deny all; } location / { try_files $uri $uri/ =404; allow 89.108.120.223; allow 89.108.122.9; allow 95.213.171.78; allow 95.213.156.46; allow 209.160.27.20; allow 89.189.163.20; #   -  amoCRM   ,   -  ,       ;)    - https://www.amocrm.ru/security/iplist.txt deny all; } location ~ \.php$ { allow 89.108.120.223; allow 89.108.122.9; allow 95.213.171.78; allow 95.213.156.46; allow 209.160.27.20; allow 89.189.163.20; #   -  amoCRM   ,   -  ,       ;)    - https://www.amocrm.ru/security/iplist.txt deny all; fastcgi_pass unix:/var/run/php5-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; fastcgi_buffers 16 16k; fastcgi_buffer_size 32k; } } 


In the folder / var / www / asterisk / (in my case) you need to create a symlink to the folder where the conversation recording files will be stored (I will tell about the recording of conversations below)

Hidden text
cd / var / www / asterisk /
ln -s / var / calls / records

A few more words about certificates. In addition to the vistep.ru.key and vistep.ru.pem already placed in their seats, we will also need dhparam.pem.

create it
 openssl dhparam -out /etc/nginx/certs/dhparam.pem 4096 


Behind this NGINX configuration, we’ll finish and proceed to the Asterisk configuration.

IP PBX Asterisk configuration


In order for amoCRM to communicate with our Asterisk, manager.conf and http.conf you need to bring it to the form:

manager.conf
 [general] enabled = yes port = 5038 bindaddr = 0.0.0.0 webenabled = yes httptimeout = 60 debug = on [amocrm] secret = JD3clEB8f4-_3ry84gJ deny = 0.0.0.0/0.0.0.0 permit = 127.0.0.1/255.255.255.0 read = cdr,reporting,originate write = reporting,originate 


http.conf
 [general] enabled=yes enablestatic=yes bindaddr=0.0.0.0 bindport=8088 prefix=asterisk 


Restart Asterisk and check if everything went up as we need

exhaust
asterisk*CLI> http show status
HTTP Server Status:
Prefix: /asterisk
Server: Asterisk/13.14.0
Server Enabled and Bound to 0.0.0.0:8088

Enabled URI's:
/asterisk/httpstatus => Asterisk HTTP General Status
/asterisk/phoneprov/... => Asterisk HTTP Phone Provisioning Tool
/asterisk/amanager => HTML Manager Event Interface w/Digest authentication
/asterisk/arawman => Raw HTTP Manager Event Interface w/Digest authentication
/asterisk/manager => HTML Manager Event Interface
/asterisk/rawman => Raw HTTP Manager Event Interface
/asterisk/static/... => Asterisk HTTP Static Delivery
/asterisk/amxml => XML Manager Event Interface w/Digest authentication
/asterisk/mxml => XML Manager Event Interface
/asterisk/ari/... => Asterisk RESTful API
/asterisk/ws => Asterisk HTTP WebSocket

Enabled Redirects:
None.
asterisk*CLI> manager show settings

Global Settings:
----------------
Manager (AMI): Yes
Web Manager (AMI/HTTP): Yes
TCP Bindaddress: 0.0.0.0:5038
HTTP Timeout (minutes): 60
TLS Enable: No
TLS Bindaddress: Disabled
TLS Certfile: asterisk.pem
TLS Privatekey:
TLS Cipher:
Allow multiple login: Yes
Display connects: Yes
Timestamp events: No
Channel vars:
Debug: Yes


An example of a dialplan (I use ael, but I am sure that anyone can translate to lua or conf if desired):

extensions.ael
 globals { WAV=/var/calls; //   WAV MP3=/var/calls; //  mp3  RECORDING=1; // , 1 - . }; macro recording (calling,called) { if ("${RECORDING}" = "1"){ Set(fname=${UNIQUEID}-${STRFTIME(${EPOCH},,%Y-%m-%d-%H_%M)}-${calling}-${called}); Set(datedir=${STRFTIME(${EPOCH},,%Y/%m/%d)}); System(mkdir -p ${WAV}/${datedir}); Set(monopt=nice -n 19 /usr/bin/lame -b 32 --silent "${WAV}/${datedir}/${fname}.wav" "${MP3}/${datedir}/${fname}.mp3" && chmod o+r "${MP3}/${datedir}/${fname}.*"); Set(CDR(filename)=${fname}.mp3); Set(CDR(recordingfile)=${fname}.wav); Set(CDR(realdst)=${called}); MixMonitor(${WAV}/${datedir}/${fname}.wav,b,${monopt}); }; }; context dial_out { //    _[71]XX => { &recording(${CALLERID(number)},${EXTEN}); Dial(SIP/${EXTEN},,tTr); Hangup(); } //    amoCRM! 100500 => { Set(DEFMAN=123); //     123 Set(TOEXT=${SHELL(wget -O - --quiet "https://vistepru.amocrm.ru/private/acceptors/asterisk_new/?redirect=Y&number=${CALLERID(num)}&USER_LOGIN=ceo@vistep.ru&USER_HASH=1dc1444b0d3172c1113ffea9078c575c")}); //     Dial(SIP/${TOEXT},,tTr); //    //      ,      if ("${DIALSTSTUS}" != "ANSWERED") { Dial(SIP/${DEFMAN},,tTr); } HangUP(); } // end 100500 _XXXXXX => { NoOP(=== CALL FROM ${CALLERID(number)} TO ${EXTEN} ===); &recording(${CALLERID(number)},${EXTEN}); Dial(SIP/83843${EXTEN}@multifon,180,tT); HangUP(); } // end of _XXXXXX _[78]XXXXXXXXXX => { NoOP(=== CALL TO ${EXTEN} ===); &recording(${CALLERID(number)},${EXTEN}); Dial(SIP/${EXTEN}@multifon,180,tT); HangUP(); }// end of _[78]XXXXXXXXXX _+7XXXXXXXXXX => { NoOP(=== CALL TO ${EXTEN} ===); &recording(${CALLERID(number)},${EXTEN}); Dial(SIP/${EXTEN}@multifon,180,tT); HangUP(); }// end of _+7XXXXXXXXXX //  ,   ,    _X. => { Hangup(); } } context default { //        _X. => { Hangup(); } }; context incoming { _[87]XXXXXXXXXX => { &recording(${CALLERID(number)},${EXTEN}); Answer(); Set(CHANNEL(musicclass)=vistep.ru); Set(CUSTOMER_NAME=${SHELL(wget -O - --quiet "https://vistepru.amocrm.ru/private/acceptors/asterisk_new/?number=${CALLERID(num)}&USER_LOGIN=ceo@vistep.ru&USER_HASH=1dc1444b0d3172c1113ffea9078c575c"|cut -d "|" -f1)}); Set(CALLERID(name)=${CUSTOMER_NAME}); Queue(queue_1,tT); NoOp(=== ${HANGUPCAUSE} ===); HangUP(); } } 


Important!

In the context of incoming (as I called the context where I process incoming calls), in a single extension, there is such a line:

 Set(CUSTOMER_NAME=${SHELL(wget -O - --quiet "https://vistepru.amocrm.ru/private/acceptors/asterisk_new/?number=${CALLERID(num)}&USER_LOGIN=ceo@vistep.ru&USER_HASH=1dc1444b0d3172c1113ffea9078c575c"|cut -d "|" -f1)}); 

This team allows us to display on the phones of employees the full name of the calling customers, picking them up from amoCRM.

Let us analyze the link from this command into components:

  1. vistepru.amocrm.ru/private/acceptors/asterisk_new ? where instead of vistepru you should have your subdomain in amocrm
  2. USER_LOGIN=ceo@vistep.ru where your (admin's) should be specified instead of my email
  3. USER_HASH = 1dc1444b0d3172c1119593ffea9078c575c where instead of my API key (in the amoCRM interface “Settings” → “API), specify your API key

Team work example

Hidden text


Now about the special extension 100500. Let me remind you that in the dialplan it looks like

So
 //    amoCRM! 100500 => { Set(DEFMAN=123); //     123 Set(TOEXT=${SHELL(wget -O - --quiet "https://vistepru.amocrm.ru/private/acceptors/asterisk_new/?redirect=Y&number=${CALLERID(num)}&USER_LOGIN=ceo@vistep.ru&USER_HASH=1dc1444b0d3172c1113ffea9078c575c")}); //     Dial(SIP/${TOEXT},,tTr); //    //      ,      if ("${DIALSTSTUS}" != "ANSWERED") { Dial(SIP/${DEFMAN},,tTr); } HangUP(); } // end 100500 


The link for wget is almost identical and the rules described above apply to it. And he needed for the so-called. “Smart call forwarding”, when the incoming call is forwarded by an employee to 100,500, and then Asterisk and amoCRM decide for themselves who to send it (read to the responsible manager or manager by default).

Why is this useful, you ask? Imagine the usual office situation:

- " "
- , , : - " "? ( !)
- ,
- .

In conjunction with amoCRM, it will look like this:

- " "
- , , 100500
- Asterisk amoCRM ,
- PROFIT!

For information, thanks to the guys from voxlink - voxlink.ru/kb/integraciya-s-crm/amocrm-asterisk

And yes, I completely forgot, if your Asterisk is not yet configured to maintain the database in MySQL, then in this article you will find all the necessary instructions.

Also, do not forget to add another field to the CDR label (you need to be able to listen to conversations on the client card in amoCRM)

Hidden text
ALTER TABLE `cdr` ADD` recordingfile` VARCHAR (120) NOT NULL

and execute a couple more commands
Spoiler header
 echo "alias recordingfile => recordingfile" >> cdr_mysql.conf asterisk -rx 'core restart now' 



AmoCRM setup


At this point, we are waiting for the greatest number of rakes, so be careful.
First of all, let's connect Asterisk in the amoCRM interface.

To do this, go to "Settings" → "Integration" → we find there Asterisk and click "Install".
We will see a description of the integration and a number of links to the guides, all this is boldly scrolling to the bottom to the input fields.

Login - amocrm (from manager.conf)
Password - JD3clEB8f4-_3ry84gJ (from manager.conf)
The script path is _https: //asterisk.vistep.ru/amocrm.php

As well as internal numbers of employees of your company.

screen


The next step is to configure the amocrm.php script.

It can be downloaded from the link in the integration description, but I want to note that the one laid out here is fixed for a specific dialplan, or rather the specific context for dial_out call origination (line 99), in order to match the Asterisk settings on the stand. Keep this in mind and change your context if it is different (this is necessary for making calls in a couple of clicks directly from amoCRM).

amocrm.php
 <?php /* amoCRM to asterisk integration. QSOFT LLC, All rights reserved. mailto: support@amocrm.com. Date: 10.04.2012 rev: 102703 Cannot be redistributed without a written permission. _____ _____ __ __ / ____| __ \| \/ | __ _ _ __ ___ ___ | | | |__) | \ / | / _` | '_ ` _ \ / _ \| | | _ /| |\/| | | (_| | | | | | | (_) | |____| | \ \| | | |_ \__,_|_| |_| |_|\___/ \_____|_| \_\_| |_(_) */ ini_set('log_errors','On'); ini_set('error_log', '/var/log/php_errors.log'); define('AC_HOST','localhost'); //   AMI/AJAM define('AC_PORT',8088); //    (  8088) . http.conf Asterisk' define('AC_PREFIX','/asterisk/'); // . http.conf Asterisk' define('AC_TLS',false); define('AC_DB_CS','mysql:host=localhost;port=3306;dbname=asterisk'); //,   MySQL   Asterisk',     define('AC_DB_UNAME','asterisk_user'); //     define('AC_DB_UPASS','232wwQd293f_2edxse3e'); //   define('AC_TIMEOUT',0.75); define('AC_RECORD_PATH','https://asterisk.vistep.ru/records/%Y/%m/%d/#'); //,       define('AC_TIME_DELTA',7); // hours. Ex. GMT+4 = 4 $db_cs=AC_DB_CS; $db_u=!strlen(AC_DB_UNAME)?NULL:AC_DB_UNAME; $db_p=!strlen(AC_DB_UPASS)?NULL:AC_DB_UPASS; date_default_timezone_set('UTC'); if (AC_PORT<1) die('Please, configure settings first!'); // die if not if (defined('AC_RECORD_PATH') AND !empty($_GET['GETFILE'])){ //get file. Do not check auth. (uniqueid is rather unique) $p=AC_RECORD_PATH; if (empty($p)) die('Error while getting file from asterisk'); try { $dbh = new PDO($db_cs, $db_u, $db_p); $sth = $dbh->prepare('SELECT calldate,recordingfile FROM cdr WHERE uniqueid= :uid'); $sth->bindValue(':uid',strval($_GET['GETFILE'])); $sth->execute(); $r = $sth->fetch(PDO::FETCH_ASSOC); if ($r===false OR empty($r['recordingfile'])) die('Error while getting file from asterisk'); $date=strtotime($r['calldate']); $replace=array(); $replace['#']=$r['recordingfile']; $dates=array('d','m','Y','y'); foreach ($dates as $d) $replace['%'.$d]=date($d,$date); // not a good idea! $p=str_replace(array_keys($replace),array_values($replace),$p); if (empty($_GET['noredirect'])) header('Location: '.$p); die($p); } catch (PDOException $e) { die('Error while getting file from asterisk'); } } // filter parameters from _GET foreach (array('login','secret','action') as $k){ if (empty($_GET['_'.$k])) die('NO_PARAMS'); $$k=strval($_GET['_'.$k]); } // trying to check accacess $loginArr=array( 'Action'=>'Login', 'username'=>$login, 'secret'=>$secret, // 'Events'=>'off', ); $resp=asterisk_req($loginArr,true); // problems? exiting if ($resp[0]['response']!=='Success') answer(array('status'=>'error','data'=>$resp[0])); //auth OK. Lets perform actions if ($action==='status'){ // list channels status $params=array( 'action'=>'status'); $resp=asterisk_req($params); // report error of any if ($resp[0]['response']!=='Success') answer(array('status'=>'error','data'=>$resp[0])); // first an last chunks are useless unset($resp[end(array_keys($resp))],$resp[0]); // renumber keys for JSON $resp=array_values($resp); // report OK answer(array('status'=>'ok','action'=>$action,'data'=>$resp)); }elseif ($action==='call'){ // originate a call $params=array( 'action'=>'Originate', 'channel'=>'SIP/'.intval($_GET['from']), 'Exten'=>strval($_GET['to']), 'Context'=>'dial_out', //was from-internal 'priority'=>'2', 'Callerid'=>'"'.strval($_GET['as']).'" <'.intval($_GET['from']).'>', 'Async'=>'Yes', // Not Implemented: //'Callernumber'=>'150', //'CallerIDName'=>'155', ); $resp=asterisk_req($params,true); if ($resp[0]['response']!=='Success') answer(array('status'=>'error','data'=>$resp[0])); answer(array('status'=>'ok','action'=>$action,'data'=>$resp[0])); } elseif ($action==='test_cdr'){ // test if DB connection params are OK. if (!class_exists('PDO')) answer(array('status'=>'error','data'=>'PDO_NOT_INSTALLED')); // we use PDO for accessing mySQL pgSQL sqlite within same algorythm try { $dbh = new PDO($db_cs, $db_u, $db_p); } catch (PDOException $e) { answer(array('status'=>'error','data'=>$e->getMessage())); } answer(array('status'=>'ok','data'=>'connection ok')); } elseif ($action==='cdr'){ // fetch call history try { $dbh = new PDO($db_cs, $db_u, $db_p); foreach (array('date_from','date_to') as $k){ $v=doubleval( (!empty($_GET[$k]))?intval($_GET[$k]):0 ); if ($v<0) $v=time()-$v; $$k=$v; } if ($date_from<time()-10*24*3600) $date_from=time()-7*24*3600; //retr. not more than 10d before $date_from=($date_from?$date_from+AC_TIME_DELTA*3600:0); //default 01-01-1970 $date_to =($date_to ?$date_to +AC_TIME_DELTA*3600:time()+AC_TIME_DELTA*3600);//default now() $sth = $dbh->prepare('SELECT calldate, src,dst,duration,billsec,uniqueid,recordingfile FROM cdr WHERE disposition=\'ANSWERED\' AND billsec>=:minsec AND calldate> :from AND calldate< :to'); // BETWEEN is illegal on some bcknds header("X-REAL_DATE:" . gmdate('Ymd H:i:s',$date_from).'@'. gmdate('Ymd H:i:s',$date_to)); $sth->bindValue(':from', date('Ymd H:i:s',$date_from) ); $sth->bindValue(':to', date('Ymd H:i:s',$date_to)); $sth->bindValue(':minsec',!empty($_GET['minsec'])?$_GET['minsec']:5,PDO::PARAM_INT); $sth->execute(); //$sth->debugDumpParams(); var_dump($sth->errorInfo()); $r = $sth->fetchAll(PDO::FETCH_ASSOC); foreach ($r as $k=>$v) $r[$k]['calldate']=date('Ymd H:i:s',strtotime($v['calldate'])-AC_TIME_DELTA*3600); answer(array('status'=>'ok','data'=>$r),true); } catch (PDOException $e) { answer(array('status'=>'error','data'=>$e->getMessage()),true); } } elseif ($action==='pop'){// fill test data. Maybe you will need it. Just comment line below. die(); $dbh = new PDO($db_cs, $db_u, $db_p); for ($i=0;$i<(int)$_GET['n'];$i++){ $array=array( date('Ymd H:i:s',time()-rand(100,7*24*3600)), 'Auto <150>', 150,'791612345678','n/a','n/a','n/a','n/a','n/a',999, rand(7,999), 'ANSWERED',3,'',uniqid(),'','','' ); $str=array(); foreach ($array as $v) $str[]="'{$v}'"; $str=implode(', ',$str); $dbh->query("INSERT INTO cdr VALUES ({$str});"); } } /** MakeRequest to asterisk interfacees * @param $params -- array of req. params * @return array -- response */ function asterisk_req($params,$quick=false){ // lets decide if use AJAM or AMI return !defined('AC_PREFIX')?ami_req($params,$quick):ajam_req($params); } /** * Shudown function. Gently close the socket */ function asterisk_socket_shutdown(){ami_req(NULL);} /*** Make request with AMI * @param $params -- array of req. params * @param bool $quick -- if we need more than action result * @return array result of req */ function ami_req($params,$quick=false){ static $connection; if ($params===NULL and $connection!==NULL) { // close connection fclose($connection); return; } if ($connection===NULL){ $en=$es=''; $connection = fsockopen(AC_HOST, AC_PORT, $en, $es, 3); // trying to connect. Return an error on fail if ($connection) register_shutdown_function('asterisk_socket_shutdown'); else {$connection=NULL; return array(0=>array('response'=>'error','message'=>'socket_err:'.$en.'/'.$es));} } // building req. $str=array(); foreach($params as $k=>$v) $str[]="{$k}: {$v}"; $str[]=''; $str=implode("\r\n",$str); // writing fwrite($connection,$str."\r\n"); // Setting stream timeout $seconds=ceil(AC_TIMEOUT); $ms=round((AC_TIMEOUT-$seconds)*1000000); stream_set_timeout($connection,$seconds,$ms); // reading respomse and parsing it $str= ami_read($connection,$quick); $r=rawman_parse($str); //var_dump($r,$str); return $r; } /*** Reads data from coinnection * @param $connection -- active connection * @param bool $quick -- should we wait for timeout or return an answer after getting command status * @return string RAW response */ function ami_read($connection,$quick=false){ $str=''; do { $line = fgets($connection, 4096); $str .= $line; $info = stream_get_meta_data($connection); if ($quick and $line== "\r\n") break; }while ($info['timed_out'] == false ); return $str; } /*** Echo`s data * @param $array answer data * @param bool $no_callback shold we output as JSON or use callback function */ function answer($array,$no_callback=false){ header('Content-type: text/javascript;'); if (!$no_callback) echo "asterisk_cb(".json_encode($array).');'; else echo json_encode($array); die(); } /** Parse RAW response * @param $lines RAW response * @return array parsed response */ function rawman_parse($lines){ $lines=explode("\n",$lines); $messages=array(); $message=array(); foreach ($lines as $l){ $l=trim($l); if (empty($l) and count($message)>0){ $messages[]= $message; $message=array(); continue;} if (empty($l)) continue; if (strpos($l,':')===false) continue; list($k,$v)=explode(':',$l); $k=strtolower(trim($k)); $v=trim($v); if (!isset( $message[$k])) $message[$k]=$v; elseif (!is_array( $message[$k])) $message[$k]=array( $message[$k],$v); else $message[$k][]=$v; } if (count($message)>0) $messages[]= $message; return $messages; } /** Make request via AJAM * @param $params req. params * @return array parsed resp. */ function ajam_req($params){ static $cookie; // EveryRequest Ajam sends back a cookir, needed for auth handling if ($cookie===NULL) $cookie=''; // make req. and store cookie list($body,$cookie)= rq(AC_PREFIX.'rawman?'.http_build_query($params),$cookie); // parse an answer return rawman_parse($body); } /** make http req. to uri with cookie, parse resp and fetch a new cookie * @param $url * @param string $cookie * @return array ($body,$newcookie) */ function rq($url,$cookie=''){ // get RAW data $r=_rq($url,$cookie); // divide in 2 parts list($headersRaw,$body)=explode("\r\n\r\n",$r,2); // parse headers $headersRaw=explode("\r\n",$headersRaw); $headers=array(); foreach ($headersRaw as $h){ if (strpos($h,':')===false) continue; list($hname,$hv)=explode(":",$h,2); $headers[strtolower(trim($hname))]=trim($hv); } // fetch cookie if (!empty($headers['set-cookie'])){ $listcookies=explode(';',$headers['set-cookie']); foreach ($listcookies as $c){ list($k,$v)=explode('=',trim($c),2); if ($k=='mansession_id') $cookie=$v; } } return array($body,$cookie); } /** mare a request to URI and return RAW resp or false on fail * @param $url * @param $cookie * @return bool|string */ function _rq($url,$cookie){ $errno=$errstr=""; $fp = fsockopen(AC_HOST, AC_PORT, $errno, $errstr, 3); if (!$fp) return false; $out = "GET {$url} HTTP/1.1\r\n"; $out .= "Host: ".AC_HOST."\r\n"; if (!empty($cookie)) $out.="Cookie: mansession_id={$cookie}\r\n"; $out .= "Connection: Close\r\n\r\n"; fwrite($fp, $out); $r=''; while (!feof($fp)) $r.=fgets($fp); fclose($fp); return $r; } 


Note!

My explanations to the parameters at the beginning of the script are given directly in the code.

You can check the work of the script using the following links (note that I use my login / password and the path to the script, they should be different here):

_https: //asterisk.vistep.ru/amocrm.php? _login = amocrm & _secret = JD3clEB8f4-_3ry84gJ & _action = test_cdr
_https: //asterisk.vistep.ru/amocrm.php? _login = amocrm & _secret = JD3clEB8f4-_3ry84gJ & _action = status
exhaust should be like on

screenshots
test_cdr

status


Test the resulting bundle


As a result of the settings made, we get the following features:


For the demonstration, the video format is best suited, so if you please:

Conclusion


I hope this article I was able to completely close the issue of integration of amoCRM and Asterisk.
If you have any questions, you are welcome in the comments.

No account on Habré? - My coordinates are profile, write, I will try to help.

Asterisk is fun!
Good luck to all!

Source: https://habr.com/ru/post/325104/


All Articles