Integratie van Asterisk en Bitrix24.

Integratie van Asterisk en Bitrix24.
Er zijn verschillende opties om IP-telefoonsystemen Asterisk en CRM Bitrix24 te integreren, maar we hebben toch besloten onze eigen te schrijven.

Functioneel is alles standaard:

  • Door op de link met het telefoonnummer van de klant in Bitrix24 te klikken, verbindt Asterisk het interne nummer van de gebruiker die de klik heeft gemaakt met het telefoonnummer van de klant. In Bitrix24 wordt een registratie van het gesprek vastgelegd en na afloop van de oproep wordt de opname van het gesprek opgehaald.
  • Wanneer er een inkomende oproep op Asterisk binnenkomt, tonen we in de interface van Bitrix24 het klantprofiel aan de medewerker naar wiens nummer de oproep is gegaan.
    Als er geen klant is, openen we het profiel voor het aanmaken van een nieuwe lead.
    Zodra het gesprek is beëindigd, registreren we dit in het profiel en halen we de opname van het gesprek op.

Onder deze sectie zal ik uitleggen hoe alles op jouw systeem ingesteld kan worden en ik zal een link naar GitHub geven — ja, neem het mee en gebruik het!

Algemene beschrijving

Onze integratie hebben we CallMe genoemd. CallMe is een kleine webapplicatie, geschreven in PHP.

Gebruikte technologieën en services

  • PHP 5.6
  • PHP AMI-bibliotheek
  • Composer
  • Nginx + php-fpm
  • supervisor
  • AMI (Asterisk Manager Interface)
  • Bitrix webhooks (vereenvoudigde implementatie van REST API)

Vooraf instellen

Op de server met Asterisk moet een webserver worden geïnstalleerd (in ons geval is dit nginx+php-fpm), supervisor en git.

Opdracht voor installatie (CentOS):

yum install nginx php-fpm supervisor git

Ga naar de map die toegankelijk is voor de webserver, haal de applicatie uit git en stel de juiste rechten in op de map:


cd /var/www
git clone https://github.com/ViStepRU/callme.git
chown nginx. -R callme/

Laten we nu nginx configureren, onze configuratie is geplaatst in

/etc/nginx/conf.d/pbx.vistep.ru.conf

server {
	server_name www.pbx.vistep.nl pbx.vistep.nl;
	listen *:80;
	rewrite ^  https://pbx.vistep.nl$request_uri? permanent;
}

server {
#        listen *:80;
#	server_name pbx.vistep.nl;


	access_log /var/log/nginx/pbx.vistep.nl.access.log main;
        error_log /var/log/nginx/pbx.vistep.nl.error.log;

    listen 443 ssl http2;
    server_name pbx.vistep.nl;
    resolver 8.8.8.8;
    ssl_stapling on;
    ssl on;
    ssl_certificate /etc/letsencrypt/live/pbx.vistep.nl/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/pbx.vistep.nl/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; # verboden voor verborgen bestanden
        }

        location ~* \/(?:uploads|files)\/.*.php$ {
                deny all; # verboden voor geüploade scripts
        }

        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 van statische bestanden
        }

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

De configuratieanalyse, beveiligingsvragen, het verkrijgen van een certificaat en zelfs de keuze van de webserver laat ik buiten deze artikel — hierover is al veel geschreven. De applicatie heeft geen beperkingen, deze werkt zowel via http als https.

Wij gebruiken https, let’s encrypt certificaat.

Als u alles correct hebt gedaan, zou u iets dergelijks moeten zien bij het volgen van de link.

Integratie van Asterisk en Bitrix24.

Configuratie van Bitrix24

Laten we twee webhooks aanmaken.

Inkomende webhook.

Onder de beheerdersaccount (met id 1) gaan we naar: Toepassingen -> Webhooks -> Voeg webhook toe -> Inkomende webhook

Integratie van Asterisk en Bitrix24.

Vul de parameters van de inkomende webhook in zoals op de schermafbeeldingen:

Integratie van Asterisk en Bitrix24.

Integratie van Asterisk en Bitrix24.

En klik op opslaan.

Na het opslaan zal Bitrix24 de URL van de inkomende webhook geven, bijvoorbeeld:

Integratie van Asterisk en Bitrix24.

Bewaar uw versie van de URL zonder de afsluitende /profile/ — deze zal in de applicatie worden gebruikt voor het werken met inkomende gesprekken.

Dit is voor mij https://b24-xsynia.bitrix24.ru/rest/1/7eh61lh8pahw0fwt/

Uitgaande webhook.

Toepassingen -> Webhooks -> Voeg webhook toe -> Uitgaande webhook

Details opnieuw op de schermafbeeldingen:

Integratie van Asterisk en Bitrix24.

Integratie van Asterisk en Bitrix24.

Sla op en ontvang de autorisatiecode

Integratie van Asterisk en Bitrix24.

Dit is voor mij xcrp2ylhzzd2v43cmfjqmkvrgrcbkni6. Dit moet u ook kopiëren, het is nodig voor het maken van uitgaande oproepen.

Belangrijk!

Op de Bitrix24-server moet een ssl-certificaat worden ingesteld (letsencrypt kan worden gebruikt), anders werkt de Bitrix API niet. Als u de cloudversie hebt, hoeft u zich geen zorgen te maken — daar is al ssl aanwezig.

Belangrijk!

In het veld 'Handleradres' moet een adres worden opgegeven dat toegankelijk is vanuit het internet!

En als laatste stap installeren we onze CallMeOut als een applicatie voor het maken van telefoongesprekken (zodat er bij een klik op het nummer een opdracht voor de oorsprong van het gesprek naar de PBX wordt gestuurd).

In het menu kiezen we: Meer -> Telefoondiensten -> Meer -> Instellingen, zetten we bij 'Standaard uitgaande belnummer' de applicatie: CallMeOut en klikken we op 'Opslaan'.

Integratie van Asterisk en Bitrix24.

Asterisk configuratie

Voor een succesvolle interactie tussen Asterisk en Bitrix24 moeten we de AMI-gebruiker callme toevoegen aan 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

Vervolgens zijn er een paar trucs die moeten worden geïmplementeerd via dialplan (in ons geval is dat extensions.ael).

Ik geef het hele bestand en daarna zal ik uitleg geven:

globals {
    WAV=\/var\/www\/pbx.vistep.ru\/callme\/records\/wav; \/\/Tijdelijke map voor WAV
    MP3=\/var\/www\/pbx.vistep.ru\/callme\/records\/mp3; \/\/Waar mp3-bestanden moeten worden opgeslagen
    URLRECORDS=https:\/\/pbx.vistep.ru\/callme\/records\/mp3;
    RECORDING=1; \/\/ Opname, 1 - ingeschakeld.
};

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()); \/\/ stel CallerID in als we deze van Bitrix24 hebben gekregen
        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 was al ingesteld ===));  
}

}


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=${STRFTTIME(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});
}

};

Laten we bij het begin beginnen: de instructie globals.

Variabele URLRECORDS bevat de URL naar de gespreksopslagbestanden, die Bitrix24 zal ophalen in het contactkaartje.

Vervolgens is de macro die we interessant vinden, recording.

Hier, naast het opnemen van gesprekken, stellen we de variabele FullFname.

Set(FullFname=${URLRECORDS}\/${datedir}\/${fname}.mp3);

Het bevat de volledige URL naar een specifiek bestand (de macro wordt overal aangeroepen).

Laten we de uitgaande oproep bekijken:

_. => {
	&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});
}

Stel dat we bellen naar 89991234567, dan komen we eerst hier terecht:

&recording(${CALLERID(number)},${EXTEN});

dat wil zeggen, de macro voor het opnemen van het gesprek wordt aangeroepen en de benodigde variabelen worden ingesteld.

Volgende

        Set(__CallIntNum=${CALLERID(num)})
	Set(CallStart=${STRFTIME(epoch,,%s)});

we registreren wie de oproep heeft geïnitieerd en noteren de starttijd van de oproep.

En na afloop, in een speciale context 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});
}

we schakelen de opname in de CDR-tabel voor deze extensie uit (het is niet nodig daar), stellen de eindtijd van de oproep in, berekenen de duur, als het resultaat van de oproep onbekend is — stellen we (variabele CallMeDISPOSITION) en, als laatste stap, sturen we alles naar Bitrix via een systeemcurl.

En nog een beetje magie — binnenkomende oproep:

888999 => {
	&recording(${CALLERID(number)},${EXTEN});
        Answer();
        ExecIF(${CallMeCallerIDName}?Set(CALLERID(name)=${CallMeCallerIDName}):NoOp()); // stel CallerID in als we het hebben gekregen van Bitrix24
        Set(CallStart=${STRFTIME(epoch,,%s)}); // begin de tijd van de oproep te tellen
        Queue(Q1,tT);
        Set(CallMeDISPOSITION=${CDR(disposition)}); 
        Hangup();
        }

Hier zijn we alleen geïnteresseerd in één regel.

ExecIF(${CallMeCallerIDName}?Set(CALLERID(name)=${CallMeCallerIDName}):NoOp());

Het geeft de PBX de opdracht om CallerID(name) in te stellen op de variabele CallMeCallerIDName.

De variabele CallMeCallerIDName wordt op zijn beurt ingesteld door de applicatie CallMe (als er in Bitrix24 een naam voor het nummer van de beller is — stellen we deze in als CallerID(name), als dat niet het geval is — doen we niets).

Instellingen van de applicatie

Configuratiebestand van de applicatie — /var/www/pbx.vistep.ru/config.php

Beschrijving van de parameters van de applicatie:

  • CallMeDEBUG — als 1 worden alle gebeurtenissen die door de applicatie worden verwerkt naar het logbestand geschreven, 0 — er wordt niets geschreven
  • tech — SIP/PJSIP/IAX/etc
  • authToken — Bitrix24 autorisatietoken, code voor de autorisatie van de uitgaande webhook
  • bitrixApiUrl — URL van de inkomende webhook, zonder profiel/
  • extenties — lijst van externe nummers
  • context — context voor het origineel van de oproep
  • listener_timeout — snelheid van gebeurtenisverwerking van Asterisk
  • asterisk — array met verbindingsinstellingen voor Asterisk:
  • host — ip of hostname van de Asterisk-server
  • schema — verbindingsschema (tcp://, tls://)
  • port — poort
  • username — gebruikersnaam
  • secret — wachtwoord
  • connect_timeout — verbindings timeout
  • read_timeout — lees timeout

voorbeeld van het configuratiebestand:

 1, // debug berichten in de log: 1 - schrijven, 0 - niet schrijven
        'tech' => 'SIP',
        'authToken' => 'xcrp2ylhzzd2v43cmfjqmkvrgrcbkni6', // Bitrix autorisatietoken
        'bitrixApiUrl' => 'https://b24-xsynia.bitrix24.ru/rest/1/7eh61lh8pahw0fwt/', // URL naar de Bitrix API (inkomende webhook)
        'extentions' => array('888999'), // lijst van externe nummers, gescheiden door komma
        'context' => 'dial_out', // uitgaande context voor het origineel van de oproep
        'asterisk' => array( // instellingen voor verbinding met 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, // snelheid van gebeurtenisverwerking van Asterisk

);

Supervisor configuratie

Supervisor wordt gebruikt om het proces dat gebeurtenissen van Asterisk CallMeIn.php verwerkt, te starten, dat inkomende oproepen volgt en met Bitrix24 interacteert (kaart tonen, kaart verbergen, enz.).

Configuratiebestand dat moet worden aangemaakt:

/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.log

Starten en opnieuw opstarten van de applicatie:

supervisorctl start callme
supervisorctl restart callme

Bekijken van de status van de applicatie:

supervisorctl status callme
callme                           RUNNING   pid 11729, uptime 17 dagen, 16:58:07

Conclusie

Het is best ingewikkeld geworden, maar ik ben ervan overtuigd dat een ervaren beheerder dit kan implementeren en zijn gebruikers kan verblijden.

Zoals beloofd, link naar GitHub.

Vragen, wensen — graag in de reacties. Laat het ook weten als je geïnteresseerd bent in hoe deze integratie is ontwikkeld, dan probeer ik in het volgende artikel alles gedetailleerder uit te leggen.

Bron: habr.com

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers 🔥 Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster