了解 FreePBX 並將其與 Bitrix24 等集成

Bitrix24 是一個巨大的組合,它結合了 CRM、工作流、會計和許多其他經理真正喜歡而 IT 員工不太喜歡的東西。 該門戶被許多中小型公司使用,包括小型診所、製造商甚至美容院。 管理者“最愛”的主要功能是電話和CRM的整合,任何來電都會立即記錄在CRM中,創建客戶名片,當來電時,會顯示客戶的信息,你可以立即看到他是誰,他在幹什麼可以賣多少,他欠多少錢。 但是來自 Bitrix24 的電話及其與 CRM 的集成需要花費金錢,有時甚至是很多。 在文章中我會告訴你與開放工具和流行的IP PBX集成的經驗 免費PBX,還要考慮各個部分的工作邏輯

我在一家銷售和配置、集成 IP 電話的公司擔任外包商。 當我被問到我們是否可以為這家公司提供一些東西來將 Bitrix24 與客戶擁有的 PBX 以及各種 VDS 公司的虛擬 PBX 集成時,我去了谷歌。 當然他給了我一個鏈接 哈布爾文章,其中有描述和 github,一切似乎都有效。 但是當嘗試使用這個解決方案時,發現 Bitrix24 已經和以前不一樣了,很多東西需要重做。 此外,FreePBX 對你來說不是一個簡單的星號,在這裡你需要考慮如何在配置文件中結合易用性和硬核撥號方案。

我們研究工作的邏輯

所以對於初學者來說,它應該如何運作。 當從 PBX 外部接收到呼叫時(來自供應商的 SIP INVITE 事件),撥號方案(撥號方案,撥號方案)的處理開始 - 呼叫的規則和順序。 從第一個數據包中,您可以獲得很多信息,然後可以在規則中使用這些信息。 分析器是研究 SIP 內部結構的絕佳工具 sngrep (鏈接) 可以通過 apt install/yum install 等簡單地安裝在流行的發行版中,但也可以從源代碼構建。 我們看看sngrep中的調用日誌

了解 FreePBX 並將其與 Bitrix24 等集成

在一個簡化的形式中,撥號方案只處理第一個數據包,有時也在對話過程中,呼叫轉移,按鈕按下(DTMF),各種有趣的事情,如 FollowMe,RingGroup,IVR 等等。

邀請包內有什麼

了解 FreePBX 並將其與 Bitrix24 等集成

實際上,大多數簡單的撥號方案都使用前兩個字段,整個邏輯圍繞著 DID 和 CallerID。 DID - 我們在哪裡打電話,CallerID - 誰在打電話。

但畢竟,我們有一家公司,而不是一部電話——這意味著 PBX 很可能在城市號碼(Ring Group)、IVR(你好,你打電話給......按一個用於...),應答機(短語),時間條件,轉發到其他號碼或單元格(FollowMe,轉發)。 這意味著很難明確地確定誰將實際接聽電話,以及當電話到達時將與誰進行對話。 這是在我們客戶的 PBX 中開始典型呼叫的示例

了解 FreePBX 並將其與 Bitrix24 等集成

呼叫成功進入 PBX 後,它會在不同的“上下文”中通過撥號方案。 從 Asterisk 的角度來看,context 是一組編號的命令,每個命令都包含一個按所撥號碼的過濾器(稱為 exten,對於初始階段的外部呼叫 exten=DID)。 撥號方案行中的命令可以是任何東西 - 內部功能(例如,呼叫內部用戶 - Dial(), 放下電話—— Hangup()), 條件運算符 (IF, ELSE, ExecIF 等),過渡到此上下文的其他規則(Goto, GotoIF),以函數調用(Gosub,Macro)的形式過渡到其他上下文。 一個單獨的指令 include имя_контекста, 它將來自另一個上下文的命令添加到當前上下文的末尾。 通過 include 包含的命令總是被執行 當前上下文的命令。

FreePBX 的整個邏輯是建立在通過包含和調用 Gosub、宏和處理程序處理程序將不同的上下文相互包含在一起的基礎上的。 考慮傳入 FreePBX 呼叫的上下文

了解 FreePBX 並將其與 Bitrix24 等集成

調用從上到下依次遍歷所有上下文,在每個上下文中都可以調用其他上下文,如宏(Macro)、函數(Gosub)或只是轉換(Goto),所以被調用的真實樹只能在日誌中被跟踪。

典型 PBX 的典型設置圖如下所示。 呼叫時,在傳入路由中搜索 DID,檢查臨時條件,如果一切正常,則啟動語音菜單。 從那裡,通過按下按鈕 1 或超時,退出到撥號操作員組。 通話結束後,hangupcall 宏被調用,之後除了特殊的處理程序(掛斷處理程序)外,撥號方案中什麼也做不了。

了解 FreePBX 並將其與 Bitrix24 等集成

在這個呼叫算法中,我們應該在哪裡提供有關 CRM 呼叫開始的信息,從哪裡開始錄音,在哪裡結束錄音並將其與有關 CRM 呼叫的信息一起發送?

與外部系統集成

什麼是 PBX 和 CRM 集成? 這些是在這兩個平台之間轉換數據和事件並將它們發送給彼此的設置和程序。 獨立系統最常見的通信方式是通過 API,而最流行的訪問 API 的方式是 HTTP REST。 但不適用於星號。

Asterisk 內部是:

  • AGI - 外部程序/組件的同步調用,主要用於撥號方案,有像這樣的庫 帕吉, PAGI

  • AMI——一個文本TCP套接字,工作原理是訂閱事件和輸入文本命令,從內部類似於SMTP,可以跟踪事件和管理調用,有一個庫 帕米 - 最流行的創建與星號的連接

AMI 輸出示例

事件:新頻道
特權:通話,所有
頻道:PJSIP/VMS_pjsip-0000078b
頻道狀態:4
ChannelStateDesc: 響鈴
來電號碼:111222
來電顯示:111222
連接線路編號:
連接線名:
語言:zh
帳戶代碼:
上下文:來自-pstn
擴展:小號
優先級:1
唯一標識:1599589046.5244
領英:1599589046.5244

  • ARI 是兩者的混合體,全部通過 REST、WebSocket,採用 JSON 格式——但有新的庫和包裝器,不是很好,隨手發現(根莖, 帕帕里),這在大約 3 年前就已經在他們的開發中了。

發起呼叫時的 ARI 輸出示例

{ "variable":"CallMeCallerIDName", "value":"111222", "type":"ChannelVarset", "timestamp":"2020-09-09T09:38:36.269+0000", "channel":{ "id »:»1599644315.5334″, «名稱»:»PJSIP/VMSpjsip-000007b6″, "state":"Ring", "caller":{ "name":"111222″, "number":"111222″ }, "connected":{ "name":"", "number" :"" }, "accountcode":"", "dialplan":{ "context":"from-pstn", "exten":"s", "priority":2, "app名稱“:”停滯“,”應用程序data":"hello-world" }, "creationtime":"2020-09-09T09:38:35.926+0000", "language":"en" }, "asteriskid":"48:5b:aa:aa:aa:aa", "application":"hello-world" }

方便或不便,使用特定 API 的可能性或不可能性取決於需要解決的任務。 與 CRM 集成的任務如下:

  • 跟踪呼叫的開始、轉移的位置、提取呼叫者 ID、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 事件

事件:新頻道

特權:通話,所有

頻道:PJSIP/VMS_pjsip-0000078b

頻道狀態:4

ChannelStateDesc: 響鈴

來電號碼:111222

來電顯示:111222

連接線路編號:

連接線名:

語言:zh

帳戶代碼:

上下文:來自-pstn

擴展:小號

優先級:1

唯一標識:1599589046.5244

領英:1599589046.5244

應用程序:設置 AppData:

通話開始=1599571046

因為 FreePBX 覆蓋了 extension.conf 和 extension_ 文件additional.conf,我們將使用該文件 延伸_習俗.conf

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

與原文章作者的原始撥號方案的特點和區別-

  • .conf 格式的撥號方案,正如 FreePBX 所希望的那樣(是的,它可以是 .ael,但不是所有版本,而且並不總是很方便)

  • 不是通過 exten=>h 處理結束,而是通過 hangup_handler 引入處理,因為 FreePBX 撥號方案只與它一起工作

  • 固定腳本調用字符串,添加引號和外部調用號碼 ExtNum

  • 處理被移動到 _custom contexts 並允許您不觸摸或編輯 FreePBX 配置 - 通過 [ext-did-自定義], 通過 [出站-allroutes-自定義]

  • 不綁定數字——文件是通用的,只需要配置路徑和鏈接到服務器

要開始,您還需要通過登錄名和密碼在 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 中,然後重新讀取配置(或重新啟動 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

初始化腳本並創建服務

由於使用 AMI 服務 Bitrix 24 的方案並不完全簡單和透明,因此必須單獨討論。 Asterisk,當 AMI 被激活時,只需打開端口即可。 當客戶端加入時,它請求授權,然後客戶端訂閱必要的事件。 事件以純文本形式出現,PAMI 將其轉換為結構化對象,並提供僅針對感興趣的事件、字段、數字等設置過濾功能的能力。

一旦調用進入,就會從父 [from-pstn] 上下文開始觸發 NewExten 事件,然後所有事件按照上下文中的行順序進行。 當從 _custom 撥號方案中指定的 CallMeCallerIDName 和 CallStart 變量接收到信息時,

  1. 查詢來電分機號碼對應的UserID功能。 如果是撥號群怎麼辦? 問題是政治性的,您是需要一次創建對每個人的呼叫(當每個人同時呼叫時)還是在輪流呼叫時創建呼叫? 大部分client都是Fisrt Available策略,所以這個沒有問題,只有一個call。 但問題需要解決。

  2. Bitrix24中的通話註冊功能,返回CallID,需要上報通話參數和錄音鏈接。 需要分機號碼或用戶 ID

了解 FreePBX 並將其與 Bitrix24 等集成

通話結束後,調用錄音下載功能,同時上報通話完成狀態(Busy、No answer、Success),同時下載帶有錄音的mp3文件鏈接(如果有的話)。

因為CallMeIn.php模塊需要持續運行,所以已經為它創建了一個SystemD啟動文件 呼叫我服務,必須放在 /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 或服務進行

# systemctl enable callme
# systemctl start callme

該服務將根據需要自行重啟(以防崩潰)。 收件箱跟踪服務不需要安裝網絡服務器,只需要 php(肯定在 FeePBX 服務器上)。 但是在沒有通過Web服務器(也有https)訪問通話記錄的情況下,將無法收聽通話記錄。

現在我們來談談撥出電話。 CallMeOut.php 腳本有兩個功能:

  • 當收到 php 腳本請求時發起呼叫(包括使用 Bitrix 本身的“呼叫”按鈕)。 沒有 web 服務器它不能工作,請求是通過 HTTP POST 接收的,請求包含一個令牌

  • 關於調用的消息、它的參數和 Bitrix 中的記錄。 當呼叫結束時,在 [sub-call-internal-ended] 撥號方案中由 Asterisk 觸發

了解 FreePBX 並將其與 Bitrix24 等集成

Web 服務器僅用於兩件事 - 下載 Bitrix 記錄文件(通過 HTTPS)和調用 CallMeOut.php 腳本。 您可以使用內置的FreePBX 服務器,其文件為/var/www/html,您可以安裝另一個服務器或指定不同的路徑。

網絡服務器

讓我們將網絡服務器設置留給獨立研究(蒂特斯, 蒂特斯, 蒂特斯). 如果你沒有域,你可以試試 FreeDomain( https://www.freenom.com/ru/index.html),這將為您的白色 IP 提供一個免費名稱(如果外部地址僅在路由器上,請不要忘記通過路由器轉發端口 80、443)。 如果您剛剛創建了一個 DNS 域,那麼您必須等待(從 15 分鐘到 48 小時)直到所有服務器都加載完畢。 根據與國內供應商合作的經驗 - 從 1 小時到一天。

安裝自動化

github 上開發了一個安裝程序,使安裝更加容易。 但它在紙面上很順利 - 當我們全部手動安裝時,因為在修改所有這些之後,它變得非常清楚什麼是朋友,誰去哪里以及如何調試它。 還沒有安裝程序

碼頭工人

如果您想快速嘗試解決方案 - Docker 有一個選項 - 快速創建一個容器,給它外部端口,滑動設置文件並嘗試(這是 LetsEncrypt 容器的選項,如果您已經有證書,你只需要將反向代理重定向到FreePBX web服務器(我們給它另一個端口是88),LetsEncrypt in docker based on 本文

您需要在下載的項目文件夾中運行該文件(在 git clone 之後),但首先進入星號配置(星號文件夾)並在那裡寫入記錄的路徑和您的站點的 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 放入腳本中呢? 我們研究了其他幾個 CRM API,尤其是免費的內置 PBX - ShugarCRM 和 Vtiger,是的! 是的,原理是一樣的。 不過這是另外一回事了,我們稍後會單獨上傳到github。

引用

免責聲明:任何與現實的相似之處都是虛構的,那不是我。

來源: www.habr.com

添加評論