Wir beschäftigen uns mit FreePBX und integrieren es nicht nur in Bitrix24

Bitrix24 ist ein riesiger Harvester, der CRM, Dokumentenfluss, Buchhaltung und viele andere Dinge kombiniert, die Manager wirklich mögen und IT-Mitarbeiter nicht wirklich mögen. Das Portal wird von vielen kleinen und mittleren Unternehmen genutzt, darunter kleine Kliniken, Fertigungsarbeiter und sogar Schönheitssalons. Die Hauptfunktion, die Manager "lieben", ist die Integration von Telefonie und CRM. Wenn ein Anruf sofort in CRM aufgezeichnet wird, Kundenkarten erstellt werden, Informationen über den Kunden beim Eingang angezeigt werden und Sie sofort sehen können, wer er ist, was er verkaufen kann und wie viel er schuldet. Die Telefonie von Bitrix24 und die Integration in CRM kostet jedoch manchmal viel Geld. In dem Artikel werde ich Ihnen die Erfahrung der Integration mit offenen Tools und der beliebten IP PBX FreePBX erläutern und auch die Logik der Arbeit verschiedener Teile berücksichtigen

Ich arbeite als Outsourcing-Unternehmen in einem Unternehmen, das IP-Telefonie verkauft und konfiguriert und integriert. Als ich gefragt wurde, ob wir diesem und diesem Unternehmen etwas anbieten könnten, um Bitrix24 in PBX-Anlagen von Kunden sowie in virtuelle PBX-Anlagen in verschiedenen VDS-Unternehmen zu integrieren, ging ich zu Google. Und er gab mir natürlich einen Link zu einem Artikel in Habr , wo es eine Beschreibung und einen Github gibt, und alles scheint zu funktionieren. Bei dem Versuch, diese Lösung zu verwenden, stellte sich jedoch heraus, dass Bitrix24 nicht mehr das ist, was es früher war, und dass noch viel überarbeitet werden muss. Außerdem ist FreePBX für Sie kein nacktes Sternchen. Hier müssen Sie darüber nachdenken, wie Sie Benutzerfreundlichkeit und einen Hardcore-Wählplan in den Konfigurationsdateien kombinieren können.

Wir studieren die Logik der Arbeit

Also zuerst, wie soll es funktionieren. Wenn ein Anruf von außerhalb an die TK-Anlage kommt (SIP INVITE-Ereignis vom Anbieter), beginnt die Verarbeitung des Wählplans (Wählplan, Wählplan) - die Regeln, was mit dem Anruf zu tun ist und in welcher Reihenfolge. Aus dem ersten Paket können viele Informationen gewonnen werden, die dann in den Regeln verwendet werden können. Ein hervorragendes Werkzeug zum Untersuchen der Innenseiten von SIP ist der sngrep- Analysator ( Link ), der einfach in gängigen Distributionen über apt install / yum install und dergleichen installiert wird. Sie können ihn jedoch auch aus dem Quellcode erstellen. Sehen wir uns das Anrufprotokoll in sngrep an

In vereinfachter Form behandelt der Wählplan nur das erste Paket, manchmal werden während des Gesprächs Anrufe weitergeleitet, Tasten gedrückt (DTMF), verschiedene interessante Dinge wie FollowMe, RingGroup, IVR und andere.

Was ist im Invite-Paket enthalten?

DID CallerID. DID - , CallerID - .

- (/ ) (Ring Group), IVR (, ... ...), (Phrases), (Time Conditions), (FollowMe, Forward). , .

"". Asterisk - , ( exten, exten=DID). - ( - Dial(), - Hangup()), (IF, ELSE, ExecIF ), (Goto, GotoIF), (Gosub, Macro). include _, . , include .

FreePBX include Gosub, Macro Handler. FreePBX

, (Macro), (Gosub) (Goto), , .

. DID, , - . 1 . hangupcall, , (hangup handler).

CRM, , CRM?

CRM? , . API, API HTTP REST. asterisk.

Asterisk :

AMI

Event: Newchannel Privilege: call,all Channel: PJSIP/VMS_pjsip-0000078b ChannelState: 4 ChannelStateDesc: Ring CallerIDNum: 111222 CallerIDName: 111222 ConnectedLineNum: ConnectedLineName: Language: en AccountCode: Context: from-pstn Exten: s Priority: 1 Uniqueid: 1599589046.5244 Linkedid: 1599589046.5244

ARI

{ "variable":"CallMeCallerIDName", "value":"111222", "type":"ChannelVarset", "timestamp":"2020-09-09T09:38:36.269+0000", "channel":{ "id":"1599644315.5334", "name":"PJSIP/VMSpjsip-000007b6", "state":"Ring", "caller":{ "name":"111222", "number":"111222" }, "connected":{ "name":"", "number":"" }, "accountcode":"", "dialplan":{ "context":"from-pstn", "exten":"s", "priority":2, "appname":"Stasis", "appdata":"hello-world" }, "creationtime":"2020-09-09T09:38:35.926+0000", "language":"ru" }, "asteriskid":"48:5b:aa:aa:aa:aa", "application":"hello-world" }

, API , . CRM :

  • , , CallerID, DID, , ( CRM)

  • , ,

  • ( ), ,

  • : CRM, FollowME ( CRM)

AMI ARI, ARI , , , AMI ( , , ). , - AMI ( ). ( , ) - ( ) PAMI. * ARI, .

, FreePBX AMI , , , , , - . PAMI -.

(s - , DID)

[ext-did-custom]

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

Event: Newchannel

Privilege: call,all

Channel: PJSIP/VMS_pjsip-0000078b

ChannelState: 4

ChannelStateDesc: Ring

CallerIDNum: 111222

CallerIDName: 111222

ConnectedLineNum:

ConnectedLineName:

Language: en

AccountCode:

Context: from-pstn

Exten: s

Priority: 1

Uniqueid: 1599589046.5244

Linkedid: 1599589046.5244

Application: Set AppData:

CallStart=1599571046

FreePBX extention.conf extention_additional.conf, extention_custom.conf

extention_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

-

  • .conf, FreePBX ( .ael, )

  • exten=>h hangup_handler, FreePBX

  • , ExtNum

  • _custom FreePBX - [ext-did-custom], [outbound-allroutes-custom]

  • -

AMI - FreePBX _custom

manager_custom.conf
;;   
[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

/etc/asterisk, ( )

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

PHP

24, AMI , . AMI . , . , PAMI , , ..

, NewExten [from-pstn], . _custom CallMeCallerIDName CallStart

  1. UserID, , . ? , ( ) ? Fisrt Available, , .

  2. 24, CallID, . UserID

, (, , ), mp3 ( ).

CallMeIn.php , SystemD callme.service, /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

systemctl service

# systemctl enable callme
# systemctl start callme

( ). , php ( FeePBX). ( https) .

. CallMeOut.php :

  • php ( "" ). , HTTP POST,

  • , . Asterisk [sub-call-internal-ended]

- ( HTTPS) CallMeOut.php. FreePBX, /var/www/html, .

(, , ). , FreeDomain( https://www.freenom.com/ru/index.html), IP ( 80, 443 , ). DNS , ( 15 48 ) . - 1 .

github , . - , , , . (

Docker

- Docker - , , ( LetsEncrypt , , FreePBX ( - 88), LetsEncrypt

( git clone), ( asterisk) URL

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:

docker-compose.yaml,

docker-compose up -d

nginx , nginx/ssl_docker.conf

CRM , . API CRM, - ShugarCRM Vtiger, ! , . , .

: ,




All Articles