アスタリスク amoCRMとの統合、ステップバイステップガイド

ネットワーク上では、記事の見出しのトピックに示されている情報のさまざまな程度の制限と完全性の指示を見つけることができますが、それらをすべてまとめる場合でも、目的のカタルシスを達成するには、まっすぐな腕、ファイル、および忍耐が必要になります。







ここでは、アスタリスクをamoCRMに接続する私の経験を、SSL証明書の受信からWebサーバーのセットアップ、そして結果のバンドルのデモで終わる、必要なすべてのニュアンスを強調するステップバイステップの指示の形で紹介します。



*せっかちな人のために、実行された操作の結果、何を参照してください

記事の終わり

入門



テストベンチにインストールされています:





SSL証明書を受け取ります



このガイドでは、Let's Encryptの無料の証明書を使用します。



当初、私はStartSSLを使用することを計画し、そこで証明書を取得するための手順を追った説明を書きましたが、それはブラウザがルート証明書を受け入れないことに気づいた後です。



それを取得する手順は非常に簡単ですが、それでもステップで説明します。



  1. letsencrypt.orgにアクセスし 、「Get Started」をクリックします



    スクリーン




  2. 次に、シェルアクセスを使用するセクションに関心があります。このセクションには、必要なすべての指示があります。



    スクリーン


  3. certbot.eff.orgにアクセスして、ソフトウェアを選択してください



    スクリーン


  4. 次に、指示に従って実行します



    コスノリのいくつかのチーム
     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. 次に、certbotユーティリティを使用して証明書要求を送信する必要があります。

    私は最も原始的な方法で行った:



    チームを運転した



     certbot certonly
          
          





    ウィザードの手順に従い、メール、webrootへのパス、ドメイン名などを示しました。

    スクリーンショット










  6. 出口で見る



    大切な
     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. 証明書をそれぞれの場所にコピーする



    非表示のテキスト
     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
          
          





コメントで正しく指摘されているように、受け取った証明書の有効期間は3か月であり、更新する必要があります。 これを考慮に入れてください!



Webサーバーのセットアップ



はじめに述べたように、NGINX Webサーバーを使用します。



私はhollywar'ovを繁殖させず、どういうわけか私の選択を動機づけます-私たちはNGINXを持ち、それを設定します。



構成の基礎は、 DimaSmirnovによる素晴らしい記事「Nginx and https。 クラスA +”を取得します。この機会に感謝します。



そのため、Webサーバーの構成ファイルの形式は次のとおりです(一部のコメントは構成で直接指定されます)。



/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; } }
      
      







フォルダ/ var / www /アスタリスク/(私の場合)では、会話録音ファイルが保存されるフォルダへのシンボリックリンクを作成する必要があります(会話レコードの設定については後述します)



非表示のテキスト
cd / var / www /アスタリスク/

ln -s / var /呼び出し/レコード



証明書についてもう少し説明します。 vistep.ru.keyとvistep.ru.pemがすでに配置されていることに加えて、dhparam.pemも必要です。



作成する
 openssl dhparam -out /etc/nginx/certs/dhparam.pem 4096
      
      







NGINX構成のsimの場合は、終了し、アスタリスクのセットアップに進みます。



IP PBXアスタリスクの構成



amoCRMがアスタリスクと通信するには、manager.confおよびhttp.confを次のように変換する必要があります。



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
      
      







アスタリスクを再起動し、必要に応じてすべてが上昇したかどうかを確認します



排気する
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








Dialplanの例(私はaelを使用していますが、必要であれば誰でもluaまたはconfに翻訳できると確信しています)



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(); } }
      
      







重要!



着信コンテキスト(着信コールを処理するコンテキストと呼ばれる)では、単一の内線に次のような行があります。



 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)});
      
      





このコマンドを使用すると、従業員の電話に呼び出し元の顧客の名前を表示し、amoCRMからそれらを取得できます。



このコマンドからコンポーネントへのリンクを分析してみましょう。



  1. vistepru.amocrm.ru/private/acceptors/asterisk_new ? vistepruの代わりに、amocrmのサブドメインを登録する必要があります
  2. USER_LOGIN=ceo@vistep.ruメールの代わりにあなた(管理者)を使用する必要があります
  3. USER_HASH = 1dc1444b0d3172c1119593ffea9078c575cここで、APIキーの代わりに(amoCRMインターフェイスの[設定]→[API]で)APIキーを指定します


チームワークの例



非表示のテキスト




次に、特別な内線番号100500について説明します。ダイヤルプランでは、



だから
 //    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
      
      







wgetのリンクはほぼ同じで、上記のルールが適用されます。 そして、彼はいわゆるのために必要です。 「スマートコールフォワーディング」。着信コールが従業員によって100500に転送され、次にアスタリスクとamoCRMが送信先を決定します(責任マネージャーまたは「デフォルト」マネージャーに送信を読み取ります)。



なぜこれが便利なのでしょうか? 典型的なオフィスの状況を想像してください:



- " "

- , , : - " "? ( !)

- ,

- .







amoCRMと組み合わせると、次のようになります。



- " "

- , , 100500

- Asterisk amoCRM ,

- PROFIT!







voxlinkの情報提供者に感謝-voxlink.ru/kb/integraciya-s-crm/amocrm-asterisk



はい、MySQLでデータベースを維持するようにアスタリスクがまだ構成されていない場合、 この記事では必要なすべての指示を見つけることができます。



また、CDRラベルに別のフィールドを追加することを忘れないでください(amoCRMのクライアントカードで会話を聞くことができる必要があります)



非表示のテキスト
ALTER TABLE `cdr` ADD` recordingfile` VARCHAR(120)NOT NULL



さらにいくつかのコマンドを実行します

ネタバレ見出し
 echo "alias recordingfile => recordingfile" >> cdr_mysql.conf asterisk -rx 'core restart now'
      
      









AmoCRMセットアップ



この時点で、最大数のレーキを待っているので、注意してください。

まず、amoCRMインターフェースでアスタリスクを接続します。



これを行うには、「設定」→「統合」に進み、そこでアスタリスクを見つけて「インストール」をクリックします。

統合の説明とガイドへのリンクが表示されます。これらはすべて、情報入力フィールドまで安全にスクロールダウンできます。



ログイン-amocrm(manager.confから)

パスワード-JD3clEB8f4-_3ry84gJ(manager.confから)

スクリプトへのパスは_httpsです://asterisk.vistep.ru/amocrm.php



あなたの会社の従業員の内部番号と同様に。



スクリーン




次のステップは、amocrm.phpスクリプトを構成することです。



統合の説明のリンクからダウンロードできますが、スタンドのアスタリスク設定に対応するために、ここに記載されている情報は特定のダイヤルプラン、またはむしろdial_out呼び出しの特定のコンテキスト(行99)に固定されていることに注意してください。 これを念頭に置いて、異なる場合はコンテキストに変更してください(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; }
      
      







注意してください!



スクリプトの先頭にあるパラメーターの説明は、コードに直接記載されています。



次のリンクを使用して、スクリプトの動作を確認できます(注-ログイン/パスワードとスクリプトへのパスを使用します。これらは異なる必要があります)。



_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

排気はオンのようにする必要があります



スクリーンショット
test_cdr



状態





結果の束をテストする



設定の結果に基づいて、次の機能を取得します。





ビデオ形式はデモンストレーションに最適ですので、お願いします:



おわりに



この記事で、amoCRMとAsteriskの統合の問題を完全に解決できたことを願っています。

ご質問があれば、コメントで歓迎します。



Habréにアカウントはありませんか? -私の座標はプロファイルです、書いて、私は助けようとします。



アスタリスクは楽しいです!

皆さんに幸運を!



All Articles