
Ci sono diverse opzioni di integrazione tra il centralino IP Asterisk e il CRM Bitrix24, ma abbiamo deciso di scrivere la nostra soluzione.
Per quanto riguarda le funzionalità, tutto è standard:
- Cliccando sul link con il numero di telefono del cliente in Bitrix24, Asterisk collega il numero interno dell'utente che ha eseguito il clic con il numero di telefono del cliente. In Bitrix24 viene registrata una nota della chiamata e, al termine della chiamata, viene recuperata la registrazione della conversazione.
- Quando arriva una chiamata su Asterisk dall'esterno, nell'interfaccia di Bitrix24 mostriamo la scheda del cliente all'operatore a cui è stata recapitata la chiamata.
Se il cliente non esiste, apriremo la scheda di creazione di un nuovo lead.
Dopo la conclusione della chiamata, lo registriamo nella scheda e recuperiamo la registrazione della conversazione.
Nel prossimo paragrafo ti mostrerò come configurare tutto e ti darò il link su GitHub — sì, prendi e usalo!
Descrizione generale
Abbiamo chiamato la nostra integrazione CallMe. CallMe è una piccola web app sviluppata in PHP.
Tecnologie e servizi utilizzati
- PHP 5.6
- Composer
- Nginx + php-fpm
- supervisor
- AMI (Asterisk Manager Interface)
- Webhook di Bitrix (implementazione semplificata di REST API)
Configurazione preliminare
Su un server con Asterisk è necessario installare un web server (noi utilizziamo nginx+php-fpm), supervisor e git.
Comando per l'installazione (CentOS):
yum install nginx php-fpm supervisor gitAccediamo alla directory accessibile al web server, cloniamo l'applicazione da git e impostiamo i diritti necessari per la cartella:
cd /var/www
git clone https://github.com/ViStepRU/callme.git
chown nginx. -R callme/
Dopo configuriamo nginx, il nostro file di configurazione si trova in
/etc/nginx/conf.d/pbx.vistep.ru.confserver {
server_name www.pbx.vistep.it pbx.vistep.it;
listen *:80;
rewrite ^ https://pbx.vistep.it$request_uri? permanent;
}
server {
# listen *:80;
# server_name pbx.vistep.it;
access_log /var/log/nginx/pbx.vistep.it.access.log main;
error_log /var/log/nginx/pbx.vistep.it.error.log;
listen 443 ssl http2;
server_name pbx.vistep.it;
resolver 8.8.8.8;
ssl_stapling on;
ssl on;
ssl_certificate /etc/letsencrypt/live/pbx.vistep.it/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/pbx.vistep.it/privkey.pem;
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/callme;
index index.php;
location ~ \/. {
deny all; # divieto per file nascosti
}
location ~* \/(?:uploads|files)\/.*.php$ {
deny all; # divieto per script caricati
}
location ~* ^.+\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|rss|atom|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ {
access_log off;
log_not_found off;
expires max; # caching della statica
}
location ~ .php {
root /var/www/callme;
index index.php;
fastcgi_pass unix:/run/php/php5.6-fpm.sock;
# fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
include /etc/nginx/fastcgi_params;
}
}
La configurazione del file, le domande sulla sicurezza, l'ottenimento del certificato e persino la scelta del server web saranno trattati separatamente — c'è già molto scritto su questi argomenti. L'applicazione non ha limitazioni, funziona sia su http che su https.
Noi utilizziamo https, certificato let's encrypt.
Se hai fatto tutto correttamente, seguendo il link dovresti vedere qualcosa di simile.

Configurazione di Bitrix24
Creeremo due webhook.
Webhook in entrata.
Sotto l'account amministratore (con id 1) segui il percorso: Applicazioni -> Webhook -> Aggiungi webhook -> Webhook in entrata.

Compila i parametri del webhook in entrata come nelle schermate:


E clicca su salva.
Dopo il salvataggio, Bitrix24 fornirà l'URL del webhook in entrata, ad esempio:

Salva la tua versione dell'URL senza il finale /profile/ — sarà utilizzato nell'applicazione per lavorare con le chiamate in arrivo.
Per me è questo https://b24-xsynia.bitrix24.ru/rest/1/7eh61lh8pahw0fwt/
Webhook in uscita.
Applicazioni -> Webhook -> Aggiungi webhook -> Webhook in uscita.
Dettagli di nuovo nelle schermate:


Salviamo e otteniamo il codice di autorizzazione.

Per me è questo xcrp2ylhzzd2v43cmfjqmkvrgrcbkni6. Anche questo deve essere copiato, serve per effettuare chiamate in uscita.
Attenzione!
Sul server Bitrix24 deve essere configurato un certificato SSL (è possibile utilizzare LetsEncrypt), altrimenti l'API di Bitrix non funzionerà. Se avete la versione cloud, non dovete preoccuparvi: è già incluso SSL.
Attenzione!
Nel campo "Indirizzo del gestore" deve essere specificato un indirizzo accessibile da Internet!
Infine, configurare CallMeOut come applicazione per effettuare chiamate (in modo che cliccando sul numero l'ATS invii il comando per l'origine della chiamata).
Nel menu selezioniamo: Altro -> Telefonia -> Altro -> Impostazioni, impostiamo in "Numero per chiamate in uscita predefinito" l'Applicazione: CallMeOut e clicchiamo su "Salva".

Configurazione di Asterisk
Per un'interazione corretta tra Asterisk e Bitrix24, dobbiamo aggiungere l'utente AMI callme in manager.conf:
[callme]
secret = JD3clEB8_f23r-3ry84gJ
deny = 0.0.0.0/0.0.0.0
permit = 127.0.0.1/255.255.255.0
permit= 10.100.111.249/255.255.255.255
permit = 192.168.254.0/255.255.255.0
read = system,call,log,verbose,agent,user,config,dtmf,reporting,cdr,dialplan
write = system,call,agent,log,verbose,user,config,command,reporting,originate
Ci sono alcune astuzie che dovremo implementare tramite il dialplan (nel nostro caso extensions.ael).
Riporto l'intero file, e poi fornirò spiegazioni:
globals {
WAV=/var/www/pbx.vistep.it/callme/records/wav; //Catalogo temporaneo per WAV
MP3=/var/www/pbx.vistep.it/callme/records/mp3; //Dove caricare i file mp3
URLRECORDS=https://pbx.vistep.it/callme/records/mp3;
RECORDING=1; // Registrazione, 1 - attivata.
};
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 ${MP3}/${datedir});
System(mkdir -p ${WAV}/${datedir});
Set(monopt=nice -n 19 /usr/bin/lame -b 32 --silent "${WAV}/${datedir}/${fname}.wav" "${MP3}/${datedir}/${fname}.mp3" && rm -f "${WAV}/${fname}.wav" && chmod o+r "${MP3}/${datedir}/${fname}.mp3");
Set(FullFname=${URLRECORDS}/${datedir}/${fname}.mp3);
Set(CDR(filename)=${fname}.mp3);
Set(CDR(recordingfile)=${fname}.wav);
Set(CDR(realdst)=${called});
MixMonitor(${WAV}/${datedir}/${fname}.wav,b,${monopt});
};
};
context incoming {
888999 => {
&recording(${CALLERID(number)},${EXTEN});
Answer();
ExecIF(${CallMeCallerIDName}?Set(CALLERID(name)=${CallMeCallerIDName}):NoOp()); // Impostiamo il CallerID se lo abbiamo riconosciuto da Bitrix24
Set(CallStart=${STRFTIME(epoch,,%s)});
Queue(Q1,tT);
Set(CallMeDISPOSITION=${CDR(disposition)});
Hangup();
}
h => {
Set(CDR_PROP(disable)=true);
Set(CallStop=${STRFTIME(epoch,,%s)});
Set(CallMeDURATION=${MATH(${CallStop}-${CallStart},int)});
ExecIF(${ISNULL(${CallMeDISPOSITION})}?Set(CallMeDISPOSITION=${CDR(disposition)}):NoOP(=== CallMeDISPOSITION già impostato ===));
}
}
context default {
_X. => {
Hangup();
}
};
context dial_out {
_[1237]XX => {
&recording(${CALLERID(number)},${EXTEN});
Set(__CallIntNum=${CALLERID(num)})
Set(CallStart=${STRFTIME(epoch,,%s)});
Dial(SIP/${EXTEN},,tTr);
Hangup();
}
_11XXX => {
&recording(${CALLERID(number)},${EXTEN});
Set(CallStart=${STRFTIME(epoch,,%s)});
Set(__CallIntNum=${CALLERID(num)});
Dial(SIP/${EXTEN:2}@toOurAster,,t);
Hangup();
}
_. => {
&recording(${CALLERID(number)},${EXTEN});
Set(__CallIntNum=${CALLERID(num)})
Set(CallStart=${STRFTIME(epoch,,%s)});
Dial(SIP/${EXTEN}@toOurAster,,t);
Hangup();
}
h => {
Set(CDR_PROP(disable)=true);
Set(CallStop=${STRFTIME(epoch,,%s)});
Set(CallMeDURATION=${MATH(${CallStop}-${CallStart},int)});
if(${ISNULL(${CallMeDISPOSITION})}) {
Set(CallMeDISPOSITION=${CDR(disposition)});
}
System(curl -s http://pbx.vistep.it/CallMeOut.php --data action=sendcall2b24 --data call_id=${CallMeCALL_ID} --data-urlencode FullFname=${FullFname} --data CallIntNum=${CallIntNum} --data CallDuration=${CallMeDURATION} --data-urlencode CallDisposition=${CallMeDISPOSITION});
}
};
Iniziamo dall'inizio: direttiva globals.
Variabile URLRECORDS memorizza l'URL dei file di registrazione delle chiamate, che Bitrix24 utilizzerà per recuperarli nella scheda contatto.
Successivamente, ci interessa il macro recording.
Qui, oltre alla registrazione delle chiamate, imposteremo la variabile FullFname.
Set(FullFname=${URLRECORDS}/${datedir}/${fname}.mp3);Essa memorizza l'URL completo di un file specifico (il macro viene chiamato ovunque).
Analizziamo la chiamata in uscita:
_. => {
&recording(${CALLERID(number)},${EXTEN});
Set(__CallIntNum=${CALLERID(num)})
Set(CallStart=${STRFTIME(epoch,,%s)});
Dial(SIP/${EXTEN}@toOurAster,,t);
Hangup();
}
h => {
Set(CDR_PROP(disable)=true);
Set(CallStop=${STRFTIME(epoch,,%s)});
Set(CallMeDURATION=${MATH(${CallStop}-${CallStart},int)});
if(${ISNULL(${CallMeDISPOSITION})}) {
Set(CallMeDISPOSITION=${CDR(disposition)});
}
System(curl -s http://pbx.vistep.ru/CallMeOut.php --data action=sendcall2b24 --data call_id=${CallMeCALL_ID} --data-urlencode FullFname=${FullFname} --data CallIntNum=${CallIntNum} --data CallDuration=${CallMeDURATION} --data-urlencode CallDisposition=${CallMeDISPOSITION});
}
Supponiamo di chiamare il 89991234567, la prima cosa che facciamo è entrare qui:
&recording(${CALLERID(number)},${EXTEN});cioè viene chiamato il macro di registrazione della chiamata e vengono impostate le variabili necessarie.
Avanti
Set(__CallIntNum=${CALLERID(num)})
Set(CallStart=${STRFTIME(epoch,,%s)});
registriamo chi ha avviato la chiamata e registriamo l'orario di inizio della chiamata.
E al termine, in un contesto speciale h
h => {
Set(CDR_PROP(disable)=true);
Set(CallStop=${STRFTIME(epoch,,%s)});
Set(CallMeDURATION=${MATH(${CallStop}-${CallStart},int)});
if(${ISNULL(${CallMeDISPOSITION})}) {
Set(CallMeDISPOSITION=${CDR(disposition)});
}
System(curl -s http://pbx.vistep.ru/CallMeOut.php --data action=sendcall2b24 --data call_id=${CallMeCALL_ID} --data-urlencode FullFname=${FullFname} --data CallIntNum=${CallIntNum} --data CallDuration=${CallMeDURATION} --data-urlencode CallDisposition=${CallMeDISPOSITION});
}
disabilitiamo la registrazione nella tabella CDR per questo interno (non è necessario), impostiamo il tempo di termine della chiamata, calcoliamo la durata, se il risultato della chiamata non è noto — impostiamo (variabile CallMeDISPOSITION) e, come ultimo passaggio, inviamo tutto a Bitrix tramite curl di sistema.
E un po' di magia in più — chiamata in arrivo:
888999 => {
&recording(${CALLERID(number)},${EXTEN});
Answer();
ExecIF(${CallMeCallerIDName}?Set(CALLERID(name)=${CallMeCallerIDName}):NoOp()); // impostiamo CallerID se lo abbiamo riconosciuto da Bitrix24
Set(CallStart=${STRFTIME(epoch,,%s)}); // iniziamo il conteggio del tempo della chiamata
Queue(Q1,tT);
Set(CallMeDISPOSITION=${CDR(disposition)});
Hangup();
}
Qui ci interessa solo una riga.
ExecIF(${CallMeCallerIDName}?Set(CALLERID(name)=${CallMeCallerIDName}):NoOp());Essa dice all'ATC di impostare CallerID(name) uguale alla variabile CallMeCallerIDName.
La variabile CallMeCallerIDName viene impostata dall'applicazione CallMe (se in Bitrix24 è presente il nome completo per il numero che chiama, lo imposteremo come CallerID(name), altrimenti non faremo nulla).
Impostazione dell'applicazione
File di configurazione dell'applicazione — /var/www/pbx.vistep.ru/config.php
Descrizione delle impostazioni dell'applicazione:
- CallMeDEBUG — se è 1, verranno registrati nel file di log tutti gli eventi elaborati dall'applicazione, 0 — non scriviamo nulla
- tech — SIP/PJSIP/IAX/etc.
- authToken — token di autenticazione di Bitrix24, codice di autorizzazione del webhook in uscita
- bitrixApiUrl — URL del webhook in entrata, senza profile/
- extentions — elenco dei numeri esterni
- context — contesto per l'origine della chiamata
- listener_timeout — velocità di elaborazione degli eventi da Asterisk
- asterisk — array con le impostazioni di connessione a Asterisk:
- host — IP o hostname del server Asterisk
- scheme — schema di connessione (tcp://, tls://)
- port — porta
- username — nome utente
- segreto — password
- connect_timeout — timeout di connessione
- read_timeout — timeout di lettura
esempio del file di configurazione:
1, // messaggi di debug nel log: 1 - scrivi, 0 - non scrivere
'tech' => 'SIP',
'authToken' => 'xcrp2ylhzzd2v43cmfjqmkvrgrcbkni6', // token di autorizzazione per Bitrix
'bitrixApiUrl' => 'https://b24-xsynia.bitrix24.ru/rest/1/7eh61lh8pahw0fwt/', // url per l'api di Bitrix (webhook in entrata)
'extentions' => array('888999'), // elenco di numeri esterni, separati da virgola
'context' => 'dial_out', // contesto in uscita per l'origine della chiamata
'asterisk' => array( // impostazioni per la connessione a Asterisk
'host' => '10.100.111.249',
'scheme' => 'tcp://',
'port' => 5038,
'username' => 'callme',
'secret' => 'JD3clEB8_f23r-3ry84gJ',
'connect_timeout' => 10000,
'read_timeout' => 10000
),
'listener_timeout' => 300, // velocità di elaborazione degli eventi da Asterisk
);Configurazione del supervisore
Il supervisore è utilizzato per avviare il processo che gestisce gli eventi da Asterisk CallMeIn.php, il quale monitora le chiamate in arrivo e interagisce con Bitrix24 (mostra scheda, nascondi scheda, ecc.).
File di configurazione da creare:
/etc/supervisord.d/callme.conf
[program:callme]
command=/usr/bin/php CallMeIn.php
directory=/var/www/pbx.vistep.ru
autostart=true
autorestart=true
startretries=5
stderr_logfile=/var/www/pbx.vistep.ru/logs/daemon.log
stdout_logfile=/var/www/pbx.vistep.ru/logs/daemon.logAvvio e riavvio dell'applicazione:
supervisorctl start callme
supervisorctl restart callmevisualizza lo stato dell'applicazione:
supervisorctl status callme
callme IN CORSO pid 11729, uptime 17 giorni, 16:58:07Conclusione
È stato piuttosto complicato, ma sono sicuro che un amministratore esperto riuscirà a implementarlo e a soddisfare i propri utenti.
Come promesso, .
Domande, suggerimenti — vi prego di scriverli nei commenti. Se siete interessati a sapere come è andato lo sviluppo di questa integrazione, scrivetelo e nel prossimo articolo cercherò di approfondire tutto in modo più dettagliato.
Fonte: habr.com
