Förstå FreePBX och integrera den med Bitrix24 och mer

Bitrix24 är en enorm kombination som kombinerar CRM, arbetsflöde, redovisning och många andra saker som chefer verkligen gillar och IT-personal inte riktigt gillar. Portalen används av många små och medelstora företag, inklusive små kliniker, tillverkare och även skönhetssalonger. Huvudfunktionen som chefer "älskar" är integrationen av telefoni och CRM, när ett samtal omedelbart spelas in i CRM skapas klientkort, vid inkommande visas information om klienten och du kan direkt se vem han är, vad han är. kan sälja och hur mycket han är skyldig. Men telefoni från Bitrix24 och dess integration med CRM kostar pengar, ibland mycket. I artikeln kommer jag att berätta om upplevelsen av att integrera med öppna verktyg och den populära IP-växeln FreePBX, och överväg också logiken i arbetet i olika delar

Jag arbetar som outsourcing på ett företag som säljer och konfigurerar, integrerar IP-telefoni. När jag fick frågan om vi kunde erbjuda något för att integrera Bitrix24 med växel som kunder har, samt med virtuella växel på olika VDS-företag, gick jag till Google. Och självklart gav han mig en länk till artikel i habr, där det finns en beskrivning, och github, och allt verkar fungera. Men när man försökte använda den här lösningen visade det sig att Bitrix24 inte längre är densamma som tidigare, och mycket behöver göras om. Dessutom är FreePBX inte en blottad asterisk för dig, här måste du fundera på hur du kombinerar användarvänlighet och en hardcore uppringningsplan i konfigurationsfiler.

Vi studerar arbetets logik

Så till att börja med, hur det hela ska fungera. När ett samtal tas emot utifrån på telefonväxeln (SIP INVITE-händelse från leverantören), börjar behandlingen av uppringningsplanen (uppringningsplan, uppringningsplan) - reglerna för vad och i vilken ordning man ska göra med samtalet. Från det första paketet kan du få mycket information, som sedan kan användas i reglerna. Ett utmärkt verktyg för att studera SIPs inre delar är analysatorn sngrep (länk) som helt enkelt installeras i populära distributioner via apt install/yum install och liknande, men den kan också byggas från källkod. Låt oss titta på samtalsloggen i sngrep

Förstå FreePBX och integrera den med Bitrix24 och mer

I en förenklad form handlar uppringningsplanen endast om det första paketet, ibland även under samtalet, samtal överförs, knapptryckningar (DTMF), diverse intressanta saker som FollowMe, RingGroup, IVR m.fl.

Vad finns i inbjudningspaketet

Förstå FreePBX och integrera den med Bitrix24 och mer

Egentligen fungerar de flesta enkla telefonplaner med de två första fälten, och hela logiken kretsar kring DID och CallerID. DID - dit vi ringer, nummerpresentation - vem ringer.

Men trots allt har vi ett företag och inte en telefon - vilket innebär att växeln med största sannolikhet har samtalsgrupper (samtidigt/på varandra följande ringning av flera enheter) på stadsnummer (Ringgrupp), IVR (Hej, du ringde ... Tryck en för ...), Telefonsvarare (Fraser), Tidsförhållanden, Vidarebefordran till andra nummer eller till en cell (FollowMe, Forward). Det gör att det är mycket svårt att entydigt avgöra vem som faktiskt ska ta emot ett samtal och vem som ska ha ett samtal med när ett samtal kommer. Här är ett exempel på början av ett typiskt samtal i våra kunders telefonväxel

Förstå FreePBX och integrera den med Bitrix24 och mer

Efter att samtalet lyckats komma in i telefonväxeln går det genom uppringningsplanen i olika "sammanhang". Sammanhanget från Asterisks synvinkel är en numrerad uppsättning kommandon, som vart och ett innehåller ett filter efter det slagna numret (det kallas exten, för ett externt samtal i inledningsskedet exten=DID). Kommandona i telefonlinjen kan vara vad som helst - interna funktioner (till exempel ringa upp en intern abonnent - Dial(), lägg ner telefonen - Hangup()), villkorsoperatörer (IF, ELSE, ExecIF och liknande), övergångar till andra regler i detta sammanhang (Goto, GotoIF), övergång till andra sammanhang i form av ett funktionsanrop (Gosub, Macro). Ett separat direktiv include имя_контекста, som lägger till kommandon från ett annat sammanhang till slutet av det aktuella sammanhanget. Kommandon som ingår via include exekveras alltid efter kommandon för det aktuella sammanhanget.

Hela logiken i FreePBX bygger på att olika sammanhang inkluderas i varandra genom att inkludera och anropa via Gosub, Macro och Handler-hanterare. Tänk på sammanhanget för inkommande FreePBX-samtal

Förstå FreePBX och integrera den med Bitrix24 och mer

Anropet går igenom alla sammanhang från topp till botten i tur och ordning, i varje sammanhang kan det finnas anrop till andra sammanhang som makron (Macro), funktioner (Gosub) eller bara övergångar (Goto), så det verkliga trädet av det som kallas kan bara spåras i loggarna.

Ett typiskt installationsschema för en typisk PBX visas nedan. Vid uppringning söks DID i inkommande rutter, tillfälliga förhållanden kontrolleras för det, om allt är i sin ordning startas röstmenyn. Från den, genom att trycka på knapp 1 eller timeout, gå ut till gruppen av uppringningsoperatörer. Efter att samtalet avslutats anropas makrot för hänguppringning, varefter ingenting kan göras i uppringningsplanen, förutom specialhanterare (uppkopplingshanterare).

Förstå FreePBX och integrera den med Bitrix24 och mer

Var i den här samtalsalgoritmen ska vi tillhandahålla information om början av samtalet till CRM, var vi ska börja spela in, var vi ska avsluta inspelningen och skicka det tillsammans med information om samtalet till CRM?

Integration med externa system

Vad är PBX- och CRM-integration? Det här är inställningar och program som konverterar data och händelser mellan dessa två plattformar och skickar dem till varandra. Det vanligaste sättet för oberoende system att kommunicera är genom API:er, och det populäraste sättet att komma åt API:er är HTTP REST. Men inte för asterisk.

Inuti Asterisk är:

  • AGI - synkront anrop till externa program/komponenter, används främst i telefonplanen, det finns bibliotek som phpagi, PAGI

  • AMI - ett text TCP-uttag som fungerar enligt principen att prenumerera på händelser och skriva in textkommandon, liknar SMTP från insidan, kan spåra händelser och hantera samtal, det finns ett bibliotek PAMI - den mest populära för att skapa en anslutning med Asterisk

Exempel på AMI-utgång

Event: Ny kanal
Privilegium: ring, alla
Kanal: PJSIP/VMS_pjsip-0000078b
Kanalstatus: 4
ChannelStateDesc: Ring
Nummerpresentation: 111222
Nummerpresentation: 111222
ConnectedLineNum:
anslutet linjenamn:
Språk: sv
kontokod:
Sammanhang: från-pstn
Exten: s
Prioritet: 1
Unik id: 1599589046.5244
Linkedid: 1599589046.5244

  • ARI är en blandning av båda, allt via REST, WebSocket, i JSON-format - men med färska bibliotek och omslag, inte särskilt bra, hittade direkt (phparia, phpari) som blev i deras utveckling för cirka 3 år sedan.

Exempel på ARI-utgång när ett samtal initieras

{ "variable":"CallMeCallerIDName", "value":"111222", "type":"ChannelVarset", "timestamp":"2020-09-09T09:38:36.269+0000", "channel":{ "id" »:»1599644315.5334″, «namn»:»PJSIP/VMSpjsip-000007b6″, "state":"Ring", "ringare":{ "namn":"111222", "nummer":"111222″ }, "ansluten":{ "namn":"", "nummer" :"" }, "accountcode":"", "dialplan":{ "context":"från-pstn", "exten":"s", "priority":2, "appname":"Stasis", "appdata":"hello-world" }, "creationtime":"2020-09-09T09:38:35.926+0000", "language":"en" }, "asteriskid":"48:5b:aa:aa:aa:aa", "application":"hello-world" }

Bekvämlighet eller olägenhet, möjligheten eller omöjligheten att arbeta med ett visst API bestäms av de uppgifter som behöver lösas. Uppgifterna för integration med CRM är följande:

  • Spåra början av samtalet, vart det överfördes, dra ut nummerpresentation, DID, start- och sluttider, kanske data från katalogen (för att söka efter en anslutning mellan telefonen och CRM-användaren)

  • Starta och avsluta inspelningen av samtalet, spara det i önskat format, informera i slutet av inspelningen var filen finns

  • Initiera ett samtal på en extern händelse (från programmet), ring ett internt nummer, ett externt nummer och anslut dem

  • Frivillig: integrera med CRM, uppringningsgrupper och FollowME för automatisk överföring av samtal i frånvaro av en plats (enligt CRM)

Alla dessa uppgifter kan lösas genom AMI eller ARI, men ARI ger mycket mindre information, det finns inte många händelser, många variabler som AMI fortfarande har (till exempel makroanrop, inställning av variabler inuti makron, inklusive samtalsinspelning) spåras inte. Därför, för korrekt och korrekt spårning, låt oss välja AMI för nu (men inte helt). Dessutom (tja, var skulle det vara utan detta, vi är lata människor) - i originalverket (artikel i habr) använd PAMI. *Då måste du försöka skriva om till ARI, men inte det faktum att det kommer att fungera.

Återuppfinna integrationen

För att vår FreePBX på enkla sätt ska kunna rapportera till AMI om samtalets början, sluttid, nummer, namn på inspelade filer är det enklast att beräkna samtalets längd med samma knep som de ursprungliga författarna - Ange dina variabler och analysera utdata för deras närvaro. PAMI föreslår att du gör detta helt enkelt genom en filterfunktion.

Här är ett exempel på hur du ställer in din egen variabel för starttiden för samtalet (s är ett speciellt nummer i uppringningsplanen som utförs INNAN du startar DID-sökningen)

[ext-did-custom]

exten => s,1,Set(CallStart=${STRFTIME(epoch,,%s)})

Ett exempel på AMI-händelse för denna linje

Event: Ny kanal

Privilegium: ring, alla

Kanal: PJSIP/VMS_pjsip-0000078b

Kanalstatus: 4

ChannelStateDesc: Ring

Nummerpresentation: 111222

Nummerpresentation: 111222

ConnectedLineNum:

anslutet linjenamn:

Språk: sv

kontokod:

Sammanhang: från-pstn

Exten: s

Prioritet: 1

Unik id: 1599589046.5244

Linkedid: 1599589046.5244

Applikation: Ställ in AppData:

CallStart=1599571046

Eftersom FreePBX skriver över filerna extension.conf och extension_additional.conf kommer vi att använda filen extension_beställnings.conf

Fullständig kod för extension_custom.conf

[globals]	
;; Проверьте пути и права на папки - юзер asterisk должен иметь права на запись
;; Сюда будет писаться разговоры
WAV=/var/www/html/callme/records/wav 
MP3=/var/www/html/callme/records/mp3

;; По этим путям будет воспроизводится и скачиваться запись
URLRECORDS=https://www.host.ru/callmeplus/records/mp3

;; Адрес для калбека при исходящем вызове
URLPHP=https://www.host.ru/callmeplus

;; Да пишем разговоры
RECORDING=1

;; Это макрос для записи разговоров в нашу папку. 
;; Можно использовать и системную запись, но пока пусть будет эта - 
;; она работает
[recording]
exten => ~~s~~,1,Set(LOCAL(calling)=${ARG1})
exten => ~~s~~,2,Set(LOCAL(called)=${ARG2})
exten => ~~s~~,3,GotoIf($["${RECORDING}" = "1"]?4:14)
exten => ~~s~~,4,Set(fname=${UNIQUEID}-${STRFTIME(${EPOCH},,%Y-%m-%d-%H_%M)}-${calling}-${called})
exten => ~~s~~,5,Set(datedir=${STRFTIME(${EPOCH},,%Y/%m/%d)})
exten => ~~s~~,6,System(mkdir -p ${MP3}/${datedir})
exten => ~~s~~,7,System(mkdir -p ${WAV}/${datedir})
exten => ~~s~~,8,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")
exten => ~~s~~,9,Set(FullFname=${URLRECORDS}/${datedir}/${fname}.mp3)
exten => ~~s~~,10,Set(CDR(filename)=${fname}.mp3)
exten => ~~s~~,11,Set(CDR(recordingfile)=${fname}.wav)
exten => ~~s~~,12,Set(CDR(realdst)=${called})
exten => ~~s~~,13,MixMonitor(${WAV}/${datedir}/${fname}.wav,b,${monopt})
exten => ~~s~~,14,NoOp(Finish if_recording_1)
exten => ~~s~~,15,Return()


;; Это основной контекст для начала разговора
[ext-did-custom]

;; Это хулиганство, делать это так и здесь, но работает - добавляем к номеру '8'
exten =>  s,1,Set(CALLERID(num)=8${CALLERID(num)})

;; Тут всякие переменные для скрипта
exten =>  s,n,Gosub(recording,~~s~~,1(${CALLERID(number)},${EXTEN}))
exten =>  s,n,ExecIF(${CallMeCallerIDName}?Set(CALLERID(name)=${CallMeCallerIDName}):NoOp())
exten =>  s,n,Set(CallStart=${STRFTIME(epoch,,%s)})
exten =>  s,n,Set(CallMeDISPOSITION=${CDR(disposition)})

;; Самое главное! Обработчик окончания разговора. 
;; Обычные пути обработки конца через (exten=>h,1,чтототут) в FreePBX не работают - Macro(hangupcall,) все портит. 
;; Поэтому вешаем Hangup_Handler на окончание звонка
exten => s,n,Set(CHANNEL(hangup_handler_push)=sub-call-from-cid-ended,s,1(${CALLERID(num)},${EXTEN}))

;; Обработчик окончания входящего вызова
[sub-call-from-cid-ended]

;; Сообщаем о значениях при конце звонка
exten => s,1,Set(CDR_PROP(disable)=true)
exten => s,n,Set(CallStop=${STRFTIME(epoch,,%s)})
exten => s,n,Set(CallMeDURATION=${MATH(${CallStop}-${CallStart},int)})

;; Статус вызова - Ответ, не ответ...
exten => s,n,Set(CallMeDISPOSITION=${CDR(disposition)})
exten => s,n,Return


;; Обработчик исходящих вызовов - все аналогичено
[outbound-allroutes-custom]

;; Запись
exten => _.,1,Gosub(recording,~~s~~,1(${CALLERID(number)},${EXTEN}))
;; Переменные
exten => _.,n,Set(__CallIntNum=${CALLERID(num)})
exten => _.,n,Set(CallExtNum=${EXTEN})
exten => _.,n,Set(CallStart=${STRFTIME(epoch,,%s)})
exten => _.,n,Set(CallmeCALLID=${SIPCALLID})

;; Вешаем Hangup_Handler на окончание звонка
exten => _.,n,Set(CHANNEL(hangup_handler_push)=sub-call-internal-ended,s,1(${CALLERID(num)},${EXTEN}))

;; Обработчик окончания исходящего вызова
[sub-call-internal-ended]

;; переменные
exten => s,1,Set(CDR_PROP(disable)=true)
exten => s,n,Set(CallStop=${STRFTIME(epoch,,%s)})
exten => s,n,Set(CallMeDURATION=${MATH(${CallStop}-${CallStart},int)})
exten => s,n,Set(CallMeDISPOSITION=${CDR(disposition)})

;; Вызов скрипта, который сообщит о звонке в CRM - это исходящий, 
;; так что по факту окончания
exten => s,n,System(curl -s ${URLPHP}/CallMeOut.php --data action=sendcall2b24 --data ExtNum=${CallExtNum} --data call_id=${SIPCALLID} --data-urlencode FullFname='${FullFname}' --data CallIntNum=${CallIntNum} --data CallDuration=${CallMeDURATION} --data-urlencode CallDisposition='${CallMeDISPOSITION}')
exten => s,n,Return

Funktion och skillnad från den ursprungliga telefonplanen för författarna till den ursprungliga artikeln -

  • Dialplan i .conf-format, som FreePBX vill ha det (ja, det kan .ael, men inte alla versioner och det är inte alltid bekvämt)

  • Istället för att bearbeta slutet genom exten=>h, introducerades bearbetning genom hangup_handler, eftersom FreePBX-uppringningsplanen bara fungerade med den

  • Fast skriptanropssträng, tillagda offerter och externt anropsnummer ExtNum

  • Bearbetning flyttas till _anpassade sammanhang och låter dig inte röra eller redigera FreePBX-konfigurationer - inkommande via [ext-gjorde-anpassad], utgående genom [utgående-allroutes-anpassad]

  • Ingen bindning till siffror - filen är universell och behöver bara konfigureras för sökvägen och länken till servern

För att komma igång behöver du även köra skript i AMI med inloggning och lösenord - för detta har FreePBX även en _custom-fil

manager_custom.conf fil

;;  это логин
[callmeplus]
;; это пароль
secret = trampampamturlala
deny = 0.0.0.0/0.0.0.0

;; я работаю с локальной машиной - но если надо, можно и другие прописать
permit = 127.0.0.1/255.255.255.255
read = system,call,log,verbose,agent,user,config,dtmf,reporting,cdr,dialplan
write = system,call,agent,log,verbose,user,config,command,reporting,originate

Båda dessa filer måste placeras i /etc/asterisk, läs sedan om inställningarna (eller starta om asterisken)

# astrisk -rv
  Connected to Asterisk 16.6.2 currently running on freepbx (pid = 31629)
#freepbx*CLI> dialplan reload
     Dialplan reloaded.
#freepbx*CLI> exit

Låt oss nu gå vidare till PHP

Initiera skript och skapa en tjänst

Eftersom schemat för att arbeta med Bitrix 24, en tjänst för AMI, inte är helt enkelt och transparent måste det diskuteras separat. Asterisk, när AMI är aktiverat, öppnar helt enkelt porten och det är allt. När en klient går med begär den auktorisering, sedan prenumererar klienten på nödvändiga händelser. Händelser kommer i vanlig text, som PAMI konverterar till strukturerade objekt och ger möjlighet att ställa in filtreringsfunktionen endast för händelser av intresse, fält, siffror, etc.

Så fort anropet kommer in, aktiveras NewExten-händelsen med start från den överordnade [från-pstn]-kontexten, sedan går alla händelser i radernas ordning i sammanhangen. När information tas emot från variablerna CallMeCallerIDName och CallStart som anges i _custom dialplan,

  1. Funktionen att begära det UserID som motsvarar anknytningsnumret dit samtalet kom. Vad händer om det är en uppringd grupp? Frågan är politisk, behöver man skapa ett samtal till alla på en gång (när alla ringer på en gång) eller skapa som de kallar när man ringer i tur och ordning? De flesta klienter har Fisrt Available-strategin, så det är inga problem med detta, bara en ringer. Men problemet måste lösas.

  2. Samtalsregistreringsfunktionen i Bitrix24, som returnerar CallID, som sedan krävs för att rapportera samtalsparametrarna och en länk till inspelningen. Kräver antingen anknytningsnummer eller UserID

Förstå FreePBX och integrera den med Bitrix24 och mer

Efter samtalets slut anropas funktionen för nedladdning av skivor, som samtidigt rapporterar status för slutfört samtal (Upptagen, Inget svar, Framgång), och även laddar ner en länk till mp3-filen med posten (om någon).

Eftersom CallMeIn.php-modulen behöver köras kontinuerligt har en SystemD-startfil skapats för den callme.service, som måste läggas i /etc/systemd/system/callme.service

[Unit]
Description=CallMe

[Service]
WorkingDirectory=/var/www/html/callmeplus
ExecStart=/usr/bin/php /var/www/html/callmeplus/CallMeIn.php 2>&1 >>/var/log/callmeplus.log
ExecStop=/bin/kill -WINCH ${MAINPID}
KillSignal=SIGKILL

Restart=on-failure
RestartSec=10s

#тут надо смотреть,какие права на папки
#User=www-data  #Ubuntu - debian
#User=nginx #Centos

[Install]
WantedBy=multi-user.target

initiering och lansering av skriptet sker genom systemctl eller tjänst

# systemctl enable callme
# systemctl start callme

Tjänsten startar om sig själv vid behov (vid kraschar). Inkorgsspårningstjänsten kräver ingen webbserver för att vara installerad, endast php behövs (som definitivt finns på FeePBX-servern). Men i avsaknad av tillgång till samtalsposter via webbservern (även med https) kommer det inte att vara möjligt att lyssna på samtalsposter.

Låt oss nu prata om utgående samtal. CallMeOut.php-skriptet har två funktioner:

  • Initiering av ett samtal när en förfrågan tas emot om ett php-skript (inklusive att använda knappen "Ring" i själva Bitrixen). Det fungerar inte utan en webbserver, begäran tas emot via HTTP POST, begäran innehåller en token

  • Meddelande om samtalet, dess parametrar och poster i Bitrix. Avfyras av Asterisk i [sub-call-internal-ended] uppringningsplanen när ett samtal avslutas

Förstå FreePBX och integrera den med Bitrix24 och mer

Webbservern behövs bara för två saker - att ladda ner Bitrix-postfiler (via HTTPS) och anropa CallMeOut.php-skriptet. Du kan använda den inbyggda FreePBX-servern, vars filer är /var/www/html, du kan installera en annan server eller ange en annan sökväg.

webbserver

Låt oss lämna webbserverinställningarna för oberoende studier (Tyts, Tyts, Tyts). Om du inte har en domän kan du prova FreeDomain( https://www.freenom.com/ru/index.html), vilket ger dig ett gratis namn för din vita IP (glöm inte att vidarebefordra portar 80, 443 genom routern om den externa adressen bara finns på den). Om du precis skapat en DNS-domän måste du vänta (från 15 minuter till 48 timmar) tills alla servrar är laddade. Enligt erfarenheten av att arbeta med inhemska leverantörer - från 1 timme till en dag.

Installationsautomation

Ett installationsprogram har utvecklats på github för att göra installationen ännu enklare. Men det var smidigt på pappret - medan vi installerar allt manuellt, eftersom det efter att ha mixtrat med allt detta blev kristallklart vad som är vänner med vem, vem som går vart och hur man felsöker det. Det finns inget installationsprogram ännu

Hamnarbetare

Om du snabbt vill prova lösningen - det finns ett alternativ med Docker - skapa snabbt en behållare, ge den portar till utsidan, skjut in inställningsfilerna och försök (detta är alternativet med LetsEncrypt-behållaren, om du redan har ett certifikat , du behöver bara omdirigera den omvända proxyn till FreePBX-webbservern (vi gav den en annan port är 88), LetsEncrypt i docker baserat på den här artikeln

Du måste köra filen i den nedladdade projektmappen (efter git-klonen), men först gå in i asterisk-konfigurationerna (asterisk-mappen) och skriv sökvägarna till posterna och webbadressen till din webbplats där

version: '3.3'
services:
  nginx:
    image: nginx:1.15-alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/ssl_docker.conf:/etc/nginx/conf.d/ssl_docker.conf
  certbot:
    image: certbot/certbot
  freepbx:
    image: flaviostutz/freepbx
    ports:
      - 88:80 # для настройки
      - 5060:5060/udp
      - 5160:5160/udp
      - 127.0.0.1:5038:5038 # для CallMeOut.php
#      - 3306:3306
      - 18000-18100:18000-18100/udp
    restart: always
    environment:
      - ADMIN_PASSWORD=admin123
    volumes:
      - backup:/backup
      - recordings:/var/spool/asterisk/monitor
      - ./callme:/var/www/html/callme
      - ./systemd/callme.service:/etc/systemd/system/callme.conf
      - ./asterisk/manager_custom.conf:/etc/asterisk/manager_custom.conf
      - ./asterisk/extensions_custom.conf:/etc/asterisk/extensions_custom.conf
#      - ./conf/startup.sh:/startup.sh

volumes:
  backup:
  recordings:

Denna docker-compose.yaml-fil körs via

docker-compose up -d

Om nginx inte startar är något fel med konfigurationen i mappen nginx/ssl_docker.conf

Andra integrationer

Och varför inte lägga in lite CRM i skript samtidigt, tänkte vi. Vi studerade flera andra CRM API:er, särskilt den gratis inbyggda telefonväxeln - ShugarCRM och Vtiger, och ja! ja, principen är densamma. Men det här är en annan historia, som vi senare kommer att ladda upp till github separat.

referenser

Friskrivningsklausul: Alla likheter med verkligheten är fiktiva och det var inte jag.

Källa: will.com

Lägg en kommentar