第十二章 HTTAPI – FreeSWITCH向Webserver尋問下一步操作

        HTTAPI允許FreeSWITCH向webserver詢問如何處理呼叫,還可以在執行之後再次詢問。因此,它實際上是一種高層次的動態協議,FreeSWITCH向webserver發送呼叫的所有相關信息和上一個動作的執行結果,webserver決定下一步做什麼。然後這個過程循環往復,直到話務被掛斷或轉移爲止。最明顯的用例是IVR,但其它類型的應用可能更喜歡這種方式。

        這一章,我們將討論以下主題:

  • HTTAPI的主要概念
  • HTTAPI的撥號方案action
  • Webserver返回的文檔結構
  • 配置mod_httapi
  • 一個簡單的PHP庫,它簡化HTTAPI應用的開發工作

HTTAPI原理

        HTTAPI允許以動態實時的方式直接控制話務,逐步執行,逐步決策。這與撥號方案或mod_xml_curl不同,它們預先定義好一個extension需要的所有執行步驟。

        當一個httpapi的撥號方案指令執行完後,FreeSWITCH會發一條HTTP請求給配置的webserver。在這條HTTP請求中,FreeSWITCH會向webserver發送話務相關的信息,和其它變量與參數。Webserver將回應FreeSWITCH一個簡短的XML HTTPAPI文檔,其中包含FreeSWITCH下一步要執行的指令FreeSWITCH將執行指令,然後再次向webserverHTTP請求。如此循環,直接話務被掛斷或轉移。

 

HTTAPI撥號方案

        在撥號方案中,通過"httapi"調用mod_httapi:

<extension name="myhttapi">
<condition field="destination_number" expression="^12345$">
<action application="answer"/>
<action application="httapi"/>
</condition>
</extension>

 

Data參數

        我們可以選擇向"httapi"傳遞一個"data"參數。在"data"參數中,可以直接寫URL(不需大括號),指定網關,它將覆蓋模塊配置文件中的值。我們也可以用大括號列出3個可能的參數,參數間以逗號分隔,以此繞過配置文件中設置的參數(和撥號串使用的技術一樣)。

<action application="httapi" data="http://localhost/freeswitch" />

 

httapi_profile

        使用此參數選擇要使用的profile,而忽略配置文件中設置爲默認profile。如果不使用此參數,並且配置文件中沒有設置默認值,則將使用名爲“default”的profile。

<action application="httapi" data="{httpapi_profile=myprofile}" />

 

url

        使用此參數來繞過配置profile裏指定的gateway-url

<action application="httapi" data="{httpapi_profile=myprofile,url=http://my.webserver.com/dir}" />

 

method

        使用此參數來繞過配置profile裏指定的method。

<action application="httapi" data="{httpapi_profile=myprofile,url=http://my.webserver.com/dir,method=POS T}" />

 

HTTAPI文檔語法

        從webserver發給mod_httapi的HTTP應答消息包含一個XML HTTAPI片段,它的最基本構成看起來是這樣的:

<document type="text/freeswitch-httapi">
<params/>
<variables/>
<work/>
</document>

 

        例如:下面是呼叫php httpapi裏的演示IVR,並按"6"時,webserver上返回的文檔內容,本章後面將詳細介紹:

<document type="text/freeswitch-httapi">
<variables>
<IVR_variable_01>VariableValue01</IVR_variable_01>
</variables>
<params>
<IVR_param_01>ParamValue01</IVR_param_01>
</params>
<variables>
<main_menu_option>6</main_menu_option>
</variables>
<work>

<playback error-file="ivr/ivr-that_was_an_invalid_entry.wav" loops="3" digit-timeout="15000" file="phrase:demo_ivr_sub_menu" name="sub_menu_option">

<bind>*</bind>
</playback>



<!-- session_id => 9c6f38d3-897a-44aa-9162-bf4f718c6d45 -->
<!-- hostname => ip-172-31-11-17 -->
<!-- Caller-Direction => inbound -->
<!-- Caller-Logical-Direction => inbound -->
<!-- Caller-Username => 1010 -->
<!-- Caller-Dialplan => XML -->
<!-- Caller-Caller-ID-Name => 1010 -->
<!-- Caller-Caller-ID-Number => 1010 -->
<!-- Caller-Orig-Caller-ID-Name => 1010 -->
<!-- Caller-Orig-Caller-ID-Number => 1010 -->
<!-- Caller-Network-Addr => 188.11.134.42 -->
<!-- Caller-ANI => 1010 -->
<!-- Caller-Destination-Number => 12345 -->
<!-- Caller-Unique-ID => 9c6f38d3-897a-44aa-9162-bf4f718c6d45 -->
<!-- Caller-Source => mod_sofia -->
<!-- Caller-Context => default -->
<!-- Caller-Channel-Name => sofia/internal/[email protected] -->
<!-- Caller-Profile-Index => 1 -->
<!-- Caller-Profile-Created-Time => 1498838395878804 -->
<!-- Caller-Channel-Created-Time => 1498838395878804 -->
<!-- Caller-Channel-Answered-Time => 1498838395898804 -->
<!-- Caller-Channel-Progress-Time => 0 -->
<!-- Caller-Channel-Progress-Media-Time => 1498838395898804 -->
<!-- Caller-Channel-Hangup-Time => 0 -->
<!-- Caller-Channel-Transfer-Time => 0 -->
<!-- Caller-Channel-Resurrect-Time => 0 -->
<!-- Caller-Channel-Bridged-Time => 0 -->
<!-- Caller-Channel-Last-Hold => 0 -->
<!-- Caller-Channel-Hold-Accum => 0 -->
<!-- Caller-Screen-Bit => true -->
<!-- Caller-Privacy-Hide-Name => false -->
<!-- Caller-Privacy-Hide-Number => false -->
<!-- url => http://localhost/phttapi/book/demo-ivr.php -->
<!-- IVR_param_01 => ParamValue01 -->
<!-- main_menu_option => 6 -->
<!-- input_type => dtmf -->
</work>
</document>

 

        HTTAPI應答必須有一個text/xml的HTTP MIME內容類型(content-type),webserver必須支持這種類型的content-type。所有HTTAPI應答必須包含type屬性值爲text/freeswitch-httapi的文檔標記。然後,可以有以下任何一個或全部標記:

  • params這些是web服務器腳本要求FreeSWITCH回傳的參數。你可以用<params>標記告訴FreeSWITCH傳遞自定義的POST參數。
  • variables<variables>標記允許你設置通道變量,可以在FreeSWITCH撥號方案中使用這些變量,或在後續的請求中讀取回httapi。
  • work這是發生最有趣事情的地方。有許多不同的action標記可用作<work>標記的子標記,全FreeSWITCH在做話務控制時能夠完成任何事情:向控制檯發日誌消息、播放聲音文檔、執行ASR、收集DTMF按鍵,等等。下一節將詳細介紹可用的action和每個action對應的屬性。

Work actions

        這一節講述HTTAPI的work actions。先做這樣的約定:*DATA*表示標記的內容(也就是<tag>*DATA*</tag>)。

        所有work actions都擁有兩個永遠可用的標記:

  • action:更改新的默認目標URL。
  • temp-action:更改目標URL以發送下一條請求。後續請求仍然使用缺省URL或action標記裏特別指定的URL。

        一些action可以容納一個或多個bind標記,這些標記的功能和bind_digit_action類似。

 

<bind action strip>*EXPR*</bind> ATTRS:

action                                  : a specific url to go to next if the binding is dialed strip     

                                            : a character to strip from the result string, such as

#

 

以下是work actions 的列表與描述:

playback

<playback file name error-file action digit-timeout input-timeout loops asr-engine asr-grammar><bind action strip>*EXPR*</bind></playback>

 

Playback播放一個文件,同時還可以收集輸入。它有以下屬性:

  • file:待播放文件的路徑
  • name:用於保存結果的參數名
  • error-file:捕獲非法輸入時播放的文件
  • digit-timeout:文件播放結束後,等待輸入的時間(如果有輸入綁定)
  • input-timeou:多位輸入時,等待下一位輸入的時間
  • loops:文件循環播放的最大次數(如果有輸入綁定)
  • asr-engine:指定自動語音識別引擎
  • asr-grammar:指定自動語音識別所使用的文法
  • terminators:收集輸入時的終止輸入符

 

示例:

<document type="text/freeswitch-httapi">
<work>
<playback action="http://newurl/index.php" temp-action="http://newtempurl/index.php" name="playback_user_input"
error-file="ivr/ivr-error.wav" file="ivr/ivr-welcome_to_freeswitch.wav" asr-engine="pocketsphinx"
asr-grammar="my_default_asr_grammar" digit-timeout="5"
input-timeout="10" loops="3" terminators="#">
<bind strip="#">~\\d{3}</bind>
</playback>
</work>
</document>

playback action 與撥號方案中的 playback APP功能類似。

record

<record file name error-file action digit-timeout input-timeout><bind action strip>*EXPR*</bind></record>

record提供錄音功能,同時還可以收集輸入,並把錄音文件發給目標URL。它有以下這些屬性:

  • file:錄音文件的路徑
  • name:用於保存結果的參數名
  • error-file:捕獲到非法輸入時播放的文件
  • beep-file:提示開始錄音所播放的文件(比如語音信箱中的“嘀”一聲)
  • digit-timeout:文件播放結束後,等待輸入的時間(如果有輸入綁定)
  • input-timeou:多位輸入時,等待下一位輸入的時間
  • limit:限制錄音時長,單位秒
  • terminators:收集輸入時的終止輸入符
  •  

示例:

<document type="text/freeswitch-httapi">
<work>
<record action="http://localhost/newurl.php" temp-action="http://localhost/newtempurl.php" name="playback_user_input"
error-file="ivr/ivr-error.wav" beep-file="tone_stream://$${beep}" file="12345.wav"
digit-timeout="5" limit="60" terminators="#">
<bind strip="#">~\\d{3}</bind>
</record>
</work>
</document>

 

record action與撥號方案的 record APP功能類似。

 

pause

<pause name error-file action digit-timeout input-timeout loops milliseconds><bind action strip>*EXPR*</bind></pause>

 

Pause爲輸入等待一段時間。它有以下屬性:

  • milliseconds:暫停的時間,單位毫秒
  • name:用於保存結果的參數名
  • error-file:捕獲到非法輸入時播放的文件
  • digit-timeout:文件播放結束後,等待輸入的時間(如果有輸入綁定)
  • input-timeou:多位輸入時,等待下一位輸入的時間
  • loops:文件循環播放的最大次數(如果有輸入綁定)
  • terminators:收集輸入時的終止輸入符

示例:

<document type="text/freeswitch-httapi">
<work>
<pause action="http://localhost/newurl.php" temp-action="http://localhost/newtempurl.php" name="pause_user_input"
error-file="ivr/it_was_that_bug.wav" digit-timeout="5" milliseconds="15000" terminators="#">
<bind strip="#">~\\d{3}</bind>
</pause>
</work>
</document>

 

 

speak

<speak file name error-file action digit-timeout input-timeout loops engine voice><bind action strip>*EXPR*</bind></speak>

 

speak調用TTS引擎向爲對方讀取一段文本,同時可以捕獲輸入。它有以下屬性:

  • text:要讀取的文本內容
  • name:用於保存結果的參數名
  • error-file:捕獲到非法輸入時播放的文件
  • digit-timeout:文件播放結束後,等待輸入的時間(如果有輸入綁定)
  • input-timeou:多位輸入時,等待下一位輸入的時間
  • loops:文件循環播放的最大次數(如果有輸入綁定)
  • engine:使用的TTS引擎
  • voice:TTS輸出的語音類別
  • terminators:收集輸入時的終止輸入符

示例:

<document type="text/freeswitch-httapi">
<work>
<speak action="http://localhost/newurl.php" temp-action="http://localhost/newtempurl.php" name="speak_user_input"
error-file="ivr/ivr-error.wav" digit-timeout="5" engine="flite"
voice="slt"
text="Hello from flite text to speech engine" terminators="#">
<bind strip="#">~\\d{3}</bind>
</speak>
</work>
</document>

 

speak action與撥號方案的 speak APP功能類似。

say

<say file name error-file action digit-timeout input-timeout loops language type method gender><bind action strip>*EXPR*</bind></say>

使用FreeSWITCH的say引擎合成模擬人類語言。它有以下屬性:

  • text:要說、拼寫、發音的文本
  • name:用於保存結果的參數名
  • error-file:捕獲到非法輸入時播放的文件
  • digit-timeout:文件播放結束後,等待輸入的時間(如果有輸入綁定)
  • input-timeou:多位輸入時,等待下一位輸入的時間
  • loops:文件循環播放的最大次數(如果有輸入綁定)
  • type:say的接口參數,指定類型
  • method:say的接口參數,指定方法
  • gender:say的接口參數,男聲/女聲之類的
  • terminators:收集輸入時的終止輸入符

 

示例:

<document type="text/freeswitch-httapi">
<work>
<say action="http://localhost/newurl.php"
temp-action="http://localhost/newtempurl.php" name="say_user_input"
error-file="ivr/ivr-error.wav" digit-timeout="5" language="en" type="name_spelled" method="pronounced"
text="This is what the caller will hear" terminators="#">
<bind strip="#">~\\d{3}</bind>
</say>
</work>
</document>

say action 和撥號方案中的 say APP功能類似。請參考第6章中的相關內容。

execute

<execute application data action>*DATA*</execute>

執行一個FreeSWITCH撥號方案APP。它有以下屬性:

  • application:待執行的撥號方案APP
  • data:APP數據的替代源
  • *DATA*:APP數據

 

示例:

<document type="text/freeswitch-httapi">
<work>
<execute action="http://localhost/newurl.php" temp-action="http://localhost/newtempurl.php" application="log"
data="INFO this is an info log message"/>
</work>
</document>

 

sms

<sms to action>DATA</sms>

發送一條sms消息,它有以下屬性:

  • *DATA*:消息數據

 

示例Example:

<document type="text/freeswitch-httapi">
<work>
<sms action="http://localhost/newurl.php"
temp-action="http://localhost/newtempurl.php" to="sip:[email protected]">Message text here</sms>
</work>
</document>

注意:它要求編譯並加載mod_sms模塊。更多信息請參考http://wiki.freeswitch.org/wiki/Mod_sms

dial

<dial context dialplan caller-id-name caller-id-number action>*DATA*</dial>

發起出局呼叫或轉移,它的屬性描述如下:

  • context: 撥號方案 context
  • dialplan:撥號方案的類別(通常是XML)
  • caller-id-name:Caller ID名字
  • caller-id-number:Caller ID號碼
  • *DATA*:呼叫號碼或發起的字符串

 

示例:

<document type="text/freeswitch-httapi">
<work><dial action="http://localhost/newurl.php"

temp-action="http://localhost/newtempurl.php" caller-id-name="HTTAPI Test"
caller-id-number="19193869900" context="default" Dialplan="XML">
sip:[email protected]
</dial>
</work>
</document>

 

Dial指令將通過撥號方案發起一路通話,如果新的call leg創建,HTTAPI所控制的呼叫將連接它。

 

recordCall

<recordCall limit name action>

 

對呼叫錄音的指令。錄音文件將在呼叫結束時發佈。它的屬性描述如下:

  • Limit:超時時間,單位秒
  • Name:如果它以http://打頭,那麼必須指定FreeSWITCH上傳文件的URL。爲了正常上傳,你的webservier必須支持PUT請求。如果省略這個屬性,FreeSWITCH將把錄音文件存放在一個臨時文件夾裏。

示例:

<document type="text/freeswitch-httapi">
<work>
<recordCall action="http://localhost/newurl.php" temp-action="http://localhost/newtempurl.php" name="http://localhost/newfile.wav" limit="60"/>
</work>
</document>

 

recordCall功能與撥號方案中的record APP類似。

 

conference

<conference profile action>

 

它開啓一個會議呼叫,參數屬性如下:

  • Profile:會議使用的profile
  • *DATA*:會議名稱

 

示例:


 

<document type="text/freeswitch-httapi">
<work>
<conference action="http://localhost/newurl.php" temp-action="http://localhost/newtempurl.php" profile="my_new_profile">
My_Conference
</conference>
</work>
</document>

它的功能與撥號方案中的conference APP類似。

 

hangup

<hangup cause action>

 

掛斷呼叫,它的屬性如下:

  • Cause:描述掛斷原因

示例:

<document type="text/freeswitch-httapi">
<work>
<hangup action="http://localhost/newurl.php" temp-action="http://localhost/newtempurl.php" cause="NORMAL_CLEARING"/>
</work>
</document>

 

它的功能與撥號方案中的hangup APP類似

 

break

跳出httapi APP並繼續執行撥號方案:


 

<document type="text/freeswitch-httapi">
<work>
<break/>
</work>
</document>

log

<log level clean action>

 

向 fs_cli、控制檯和日誌文件寫一條日誌

  • level:日誌級別
  • clean:如果設置爲true,那麼日誌行不打印日誌前綴

 

示例:

<document type="text/freeswitch-httapi">
<work>
<log action="http://localhost/newurl.php"
temp-action="http://localhost/newtempurl.php" level="info">this is a log message with a prefix</log>
<log level="warning"
clean="1">and this is one without</log>
</work>
</document>

 

它的功能與撥號方案中的log APP類似。需要注意的是撥號方案APP沒有clean屬性,而httapi的log指令有。

 

continue

<continue action>

 

繼續執行,沒有指定具體的work action(也就是說,它是一條無操作的指令)。如果你希望根據getVar或類似的結果請求不同的action URL,這是非常有用的。


 

<document type="text/freeswitch-httapi">
<work>
<continue action="http://localhost/newurl.php"
temp-action="http://localhost/newtempurl.php"/>
</work>
</document>

getVar

<getVar action temp-action permanent>

 

提取一個通道變量的內容(取決於權限)。它的屬性如下:

  • permanent:如果設置爲true,那麼這個變量在這路通話的所有後續HTTAPI請求中都會發送,否則,只在下一個請求中發。
  • name:需要讀取的通道變量名(比如說caller_id_name)

 

示例:

<document type="text/freeswitch-httapi">
<work>
<getVariable name="caller_id_name" action="http://localhost/newurl.php"
temp-action="http://localhost/newtempurl.php" permanent="1"/>
</work>
</document>

 

 

voicemail

<voicemail action temp-action check auth-only profile domain id>

 

它在不需要“執行”權限的情況下調用撥號方案的voicemail APP。它的屬性描述如下:

  • check:如果設置值爲true,那麼允許主叫方提取信箱留言消息,也就是說,它表明主叫方是語音信箱的用戶。如果省略這個屬性,那麼系統將提示主叫方留言。
  • auth-only:只驗證身份,然後繼續。在選擇此模式的情況下,成功驗證後將在通道上設置兩個新變量:
    • variable_user_pin_authenticated,值設爲true
    • variable_user_pin_authenticated_user,值爲通過驗證的用戶名
      • profile:使用的Voicemail profile名(忽略缺省的"default")。
      • domain:使用的域(忽略全局變量配置的域)
      • id:使用的ID(省略提示輸入ID)

示例:

<document type="text/freeswitch-httapi">
<work>
<voicemail action="http://localhost/newurl.php" temp-action="http://localhost/newtempurl.php" auth-only="1"
check="1" domain="192.168.1.101" id="1010"
profile="default"/>
</work>
</document>

 

它的功能類似於撥號方案中的voicemail APP。

 

vmname

vmname播放一個語音信箱的名字,同時收集輸入。它的屬性:

  • id:要播放的用戶名,以user@domain格式傳遞
  • name:保存結果的參數名稱
  • error-file:捕獲非法輸入時播放的提示音
  • digit-timeout:文件播放結束後,等待輸入的時長(當綁定輸入時)
  • input-timeout:多位輸入時,等待下一位輸入的時間
  • loops:嘗試循環的最大次數
  • terminators:結束輸入的標識符

 

示例:

<document type="text/freeswitch-httapi">
<work>
<vmname action="http://newurl/index.php" temp-action="http://newtempurl/index.php" name="vmname_user_input"
error-file="ivr/ivrerror.wav" id="[email protected]"
digit-timeout="5" input-timeout="10" loops="3" terminators="#">
<bind strip="#">~\\d{3}</bind>
</vmname>
</work>
</document>

exiting (參數)

        用戶掛斷時,FreeSWITCH將向webserver傳遞一個"exiting"參數,通知話務已經結束,這時你可以刪除任何可能打開的相關會話,並完成跟蹤報告。雖然你可以完全忽視這個請求,FreeSWITCH可以自我恢復,但是,最好來是回覆一個純文本的"OK"。

跨越連續請求存儲數據

        在一個請求到下一個請求間存儲作息片段的最佳方式是什麼?顯然,你可以設置並獲取通道變量,但是,每次set/get操作需要處理幾個請求,這個代價是昂貴的

          你只需把這些信息存儲在一個會話中,以便後續訪問。每個請求都將包含一個session_id post或get參數。使用這個session_id參數值,你應該能夠以它的值作爲會話標識符初始化會話。幾乎每種web編程語言都支持這種級別的控制。下面是一個PHP的代碼實例:

if ( array_key_exists( 'session_id', $_REQUEST ) ) { session_id( $_REQUEST['session_id'] );

}
session_start();

 

        啓動會話之後,您將有權訪問會話變量或對象,該變量或對象可用於存儲在後續請求中需要的信息。

 

mod_httapi配置文件

        在conf/autoload_configs有指定mod_httapi的配置文件,即httapi.conf.xml。它包含幾個settings參數,還有一個profiles。示例配置中包含一個名字叫defaultprofile (你可以建立自己的profile)。

params標記

        在profile 標記內部,你會注意到有許多param條目。它們控制了HTTP請求所用的缺省配置缺省URL

 

gateway-url

        gateway-url參數設置了mod_httapi將發起請求的缺省資源。它可以在撥號方案中被覆蓋,httapi APP調用時可指定參數。

 

method

        method參數設置mod_httapi請求的缺省方法(GET|POST|PUT)。撥號方案可以覆蓋這個參數設置,以花括號格式指定參

 

permissions標記

        你可能需要擁有一些控制權限,比如不讓改變變量值,或者不希望無意間執行某個APP或API。

        在示例配置文件httapi.conf.xml中,有permissions標記,在這個標記內,你會發現有許多可以啓用的權限開關,對其中一些方面進行更細粒度的ACL樣式控制。

        需要注意的一點是,只要關閉<permission>標記而不包括列表,就可以跳過下面我們將看到的任何類似於ACL的控制列表。這個的缺省行爲是允許所有,就像你創建一個列表,並設定default="allow"那樣。以下兩個示例的工作方式完全相同:

示例一:
 

<permission name="set-vars" value="true"/>

示例二:

<permission name="set-vars" value="true">
<variable-list default="allow">
</variable-list>
</permission>

 

set-params

        它允許你設置或限制撥號方案中調用httapi時以{}設定的參數,並在呼叫的生命週期中保持。

 

set-vars

        它允許你設置或限制通道變量。這個權限比set-params更進一步,它允許你指定哪些變量是可以設置的,類似於acl.conf.xml裏的訪問控制權限。請注意示例配置中的這個片段所設置的缺省允許訪問的策略和能力:

<permission name="set-vars" value="true">
<variable-list default="deny">
<!-- Variables here may be changed -->
<variable name="caller_id_name"/>
</variable-list>
</permission>

        上面列出的代碼實際上表明,除非在變量列表中指定了變量,否則設置變量的能力將被禁用。因此,變量列表中的變量是默認建立規則的例外:

<permission name="set-vars" value="true">
<variable-list default="allow">
<!-- Variables here may *not* be changed -->
<variable name="caller_id_name"/>
</variable-list>
</permission>

 

get-vars

        允許你從呼叫中讀取通道變量。這個權限的控制粒度與set-vars相同。

 

extended-data

        有了它,可以向Web應用程序傳遞更多的信息。默認行爲是發佈一個通道的簡要概述,然後允許你通過HTTAPI命令和後續的回調函數獲取你所需要的信息。如果你希望在初始請求中把所有通道變量post給你的應用程序,那麼你需要啓用這個選項。

 

extended-data超過CGI允許的最大長度

        如果你在配置profile打開extended-data,那麼在你嚮應用程序傳遞數據的過程中可能會遺漏掉某些內容。原因是缺省的數據傳遞方法是GET請求。通過extended-data傳遞的所有信息長度經常會超過CGI參數所允許的最大長度,結果導致請求中超長的部分數據被截斷。有幾種方式可以解決這個問題,其中最持久的方式是在httapi.conf.xml中設置method參數:

 

<param name="method" value="POST"/>

 

        此外,如果你只想在超長的請求中使用POST方法(其它的請求還是使用GET),那麼你可以在調用httapi時單獨爲每個請求設置method。例如:

<action application="httapi" data="{url=http://localhost/httapi/index.php,method=POST}"/>

 

execute-apps

        如果你設置了這個權限,那麼你將可以在httapi web應用程序中調用撥號方案的APP。這將允許你通過<execute>標記使用APP,就如之前Work actions的語法描述那樣。這個權限與我們之前見過的幾個權限一樣,有類似ACL的控制。讓我們在缺省策略中拒絕APP訪問,然後單獨開放info和hangup這兩個APP的權限:

<permission name="execute-apps" value="true">
<application-list default="deny">
<application name="info"/>
<application name="hangup"/>
</application-list>
</permission>

 

 

expand-vars

        這個權限允許你在web應用程序中像XML撥號方案那樣使用變量。像${caller_id_number}這類變量將以內聯方式展開。表達式${caller_id_number}將爲你提供呼叫方的號碼。這也爲你提供了一種在web應用程序中使用API命令的方法地。看一下這個實例:

${sofia_contact([email protected])}

 

        這行代碼將以給定的參數執行sofia_contact API命令,並在恰當的位置插入執行結果。

 

        類似ACL的控制列表依然可用。你可以根據需要允許或禁用任意多個API命令或變量。下面XML片段是一個實例:

<permission name="expand-vars" value="true">
<variable-list default="deny">
<variable name="caller_id_name"/>
<variable name="caller_id_number"/>
</variable-list>
<api-list default="deny">
<api name="expr"/>
<api name="lua"/>
<api name="sofia_contact"/>
</api-list>
</permission>

 

        前面這段代碼允許設置caller_id_name和caller_id_number這兩個通道變量,其它的則不允許。它允許執行expr、lua和sofia_contact 這幾個API命令,其它的則不允許。這個示例展示了作爲應用程序開發人員和FreeSWITCH系統管理員對系統上運行的HTTAPI程序的精細粒度控制。

dial

        允許你從web應用程序撥號,它將觸發撥號方案相應的路由。即使在配置文件中設置爲false,也可以動態啓用以下幾個選項之一,進而啓用撥號權限:dial-set-context、dial-set-Dialplan、dial-set-cid- name、dial-set-cid-number或dial-full-originate。

 

dial-set-dialplan

        允許你在dial標記範圍內改變撥號方案的類別。缺省條件下,總是使用"XML"撥號方案。

 

dial-set-context

        允許你在dial標記範圍內改變撥號方案的context。這將允許使用:

<dial context="othercontext">

 

dial-set-cid-name

        允許你在dial標記範圍內改變callerid名字。這將允許使用:

<dial cid_name="Giovanni Maruzzelli">

 

dial-set-cid-number

        允許你在dial標記範圍內改變callerid號碼。這將允許使用:

<dial cid_number="+393472665618">

 

dial-full-originate

         允許使用完整的endpoint/profile/number語法進行撥號。(比如sofia/internal/[email protected])

conference

它將允許你呼入會議。

 

conference-set-profile

        允許你在conference標記範圍內改變profile名字。這將允許使用:

<conference profile="wb">

         如果啓用了conference-set-profile,那麼即使在配置文件的其他地方將conference設置爲false,也將啓用conference

 

PHP和Python的HTTPAPI開發庫

          當你在看本章前面的示例時,你或許會想:雖然httapi的功能非常強大,但是你確實不願意學習一種新的XML格式來控制FreeSWITCH。此外,手工打印這些XML也是一個大麻煩。這就是爲什麼httapi XML會用你所選擇的語言寫爲容易實現的、帶有幫助文檔的開發庫的原因。

        已經有幾個這樣的開發庫實現,有PHP的,也有Python的。這兩個庫都是Raymond Chandler開發的,你可以在FreeSWITCH GIT倉庫的intralanman目錄下找到它們。你可以通過下面命令下載開發庫和示例(還有其它很多好東西):

cd /usr/src

git clone https://freeswitch.org/stash/scm/fs/freeswitch-contrib.git cd freeswitch-contrib/intralanman

 

         下載後,你將發現一個PHP目錄和一個Python目錄。PHP庫和Python庫都只由一個文件組成(phttapi.php 和 httapy.py),使用起來很方便。

 

 

PHP-HTTAPI版的演示IVR

          開始之前,你需要需要搭建支持PHP的web服務器,並安裝PHP XML擴展。接下來需要的就是PHTTAPI庫。

 

          我們的演示腳本叫"demo-ivr.php",我們把庫放到它的父目錄中,在腳本的第二行通過"required"語句引用庫:

<?php
require "../phttapi.php";


if ( array_key_exists( 'session_id', $_REQUEST ) ) { session_id( $_REQUEST['session_id'] );
}

session_start();


if ( array_key_exists( 'exiting', $_REQUEST ) ) { header( 'Content-Type: text/plain' );
print "OK"; exit();
}



$demo = new phttapi();
$opt    = array_key_exists( 'main_menu_option', $_REQUEST ) ?
$_REQUEST['main_menu_option'] : '';



$demo->start_variables();
$demo->add_variable( 'IVR_variable_01', 'VariableValue01' );
$demo->end_variables();
$demo->start_params();
$demo->add_param( 'IVR_param_01', 'ParamValue01' );
$demo->end_params();


if ( preg_match( '/^10[01][0-9]$/', $opt ) ) {
$xfer = new phttapi_dial( $opt );
$xfer->context( 'default' );
$xfer->dialplan( 'XML' );
$demo->add_action( $xfer );
} else {
switch ( $opt ) { case '1':
$conf = new phttapi_dial( '9888' );
$conf->caller_id_name( 'another book reader' );
$conf->context( 'default' );
$conf->dialplan( 'XML' );
$demo->add_action( $conf ); break;


case '2':
$echo = new phttapi_dial( '9196' );
$echo->context( 'default' );
$echo->dialplan( 'XML' );
$demo->add_action( $echo );


break;

case '3':
$moh = new phttapi_dial( '9664' );
$moh->context( 'default' );
$moh->dialplan( 'XML' );
$demo->add_action( $moh ); break;


case '4':
$clue = new phttapi_dial( '9191' );
$clue->caller_id_name( 'another book reader' );

$clue->context( 'default' );
$clue->dialplan( 'XML' );
$demo->add_action( $clue ); break;


case '5':
$monkey = new phttapi_dial( '1234*256' );
$monkey->dialplan( 'enum' );
$demo->add_action( $monkey ); break;


case '6':
if ( array_key_exists( 'sub_menu_option', $_REQUEST ) &&
$_REQUEST['sub_menu_option'] == '*' ) {
unset( $_SESSION['first_sub_play_done'] );
$demo->add_action( $c = new phttapi_continue() ); break;
}

$demo->start_variables();
$demo->add_variable( 'main_menu_option', 6 );
$demo->end_variables();


$sub = new phttapi_playback();
$sub->error_file( 'ivr/ivr-that_was_an_invalid_entry.wav' );
$sub->loops( 3 );
$sub->digit_timeout( '15000' );


if ( !array_key_exists( 'first_sub_play_done', $_SESSION ) ) {
$_SESSION['first_sub_play_done'] = TRUE;
$sub->file( 'phrase:demo_ivr_sub_menu' );
} else {
$sub->file( 'phrase:demo_ivr_sub_menu_short' );

}



$star = new phttapi_action_binding( '*' );
$sub->add_binding( $star );
$sub->name( 'sub_menu_option' );
$demo->add_action( $sub ); break;


case '9':
$continue = new phttapi_continue();
$demo->add_action( $continue ); break;


default:
$intro = new phttapi_playback();
$intro->error_file( 'ivr/ivr-that_was_an_invalid_entry.wav' );
$intro->loops( 3 );
$intro->digit_timeout( '2000' );
$intro->input_timeout( '10000' );
$intro->name( 'main_menu_option' );


if ( !array_key_exists( 'first_play_done', $_SESSION ) ) {
$_SESSION['first_play_done'] = TRUE;
$intro->file( 'phrase:demo_ivr_main_menu' );
} else {
$intro->file( 'phrase:demo_ivr_main_menu_short' );
}



$b1=new phttapi_action_binding(1);
$b2=new phttapi_action_binding(2);
$b3=new phttapi_action_binding(3);
$b4=new phttapi_action_binding(4);
$b5=new phttapi_action_binding(5);
$b6=new phttapi_action_binding(6);
$b9=new phttapi_action_binding(9);
$bext = new phttapi_action_binding( '~10[01][0-9]' );
$bext->strip( '#' );


$intro->add_binding( $bext );
$intro->add_binding( $b1 );
$intro->add_binding( $b2 );
$intro->add_binding( $b3 );
$intro->add_binding( $b4 );
$intro->add_binding( $b5 );
$intro->add_binding( $b6 );
$intro->add_binding( $b9 );

$demo->add_action( $intro );
}

}

header( 'Content-Type: text/xml' ); foreach ( $_REQUEST as $key => $val ) {
$demo->comment( " $key => $val " );
}



print $demo->output();

 

         將演示腳本複製到web服務器上的web documents目錄中。

        請跟隨我們的腳步,逐行討論代碼的功能。

 

if ( array_key_exists( 'session_id', $_REQUEST ) ) { session_id( $_REQUEST['session_id'] );

}

session_start();

 

        這段代碼將用session_id開啓一個會話,就如我們在這一章前面討論的那樣。

if ( array_key_exists( 'exiting', $_REQUEST ) ) { session_destroy();
header( 'Content-Type: text/plain' ); print "OK";
exit();
}

 

         如果我們看到exiting參數,那麼直接銷燬PHP會話,並告訴FreeSWITCH我們已經理解其意圖,隨後退出腳本。


 

$demo = new phttapi();

       這裏我們創建httapi對象。這個對象($demo)允許我們執行work actions的操作。


 

$opt = array_key_exists( 'main_menu_option', $_REQUEST ) ?

$_REQUEST['main_menu_option'] : '';

這是一個簡單的if/then/else條件判斷語句,它將確保始終設置$opt,即使main_menu_option爲空。我們後續將綁定main_menu_option的選項,構成將來用戶按鍵的響應代碼。

if ( preg_match( '/^10[01][0-9]$/', $opt ) ) {

$xfer = new phttapi_dial( $opt );

$xfer->context( 'default' );

$xfer->Dialplan( 'XML' );

$demo->add_action( $xfer );

} else {

 

這段代碼測試選項是否與extension的正則表達式匹配。如果匹配,構建一個新的phttapi_dial對象($xfer),設置目的地,並向$demo對象添加action。如果它與extension不匹配,那麼進入一個switch判斷分支,測試每個單位的按鍵選項。


 

case '1':

$conf = new phttapi_dial( '9888' );

$conf->caller_id_name( 'another book reader' );

$conf->context( 'default' );

$conf->Dialplan( 'XML' );

$demo->add_action( $conf ); break;

          如果是按鍵1,那麼創建一個撥號選項,它對應於我們在本章前面描述的撥號標記。標記上的每個屬性在phttapi_dial類裏面有一個對應的方法。比如說,context方法設置context屬性;Dialplan方法設置Dialplan屬性,以此類推。(選項1將把話務發給公共的FreeSWITCH 會議服務)

        選項2到選項5這幾個分支都是撥號對象,它們具體相同的基礎邏輯,但賦予不同的屬性值,以實現每個選項所期望的結果。

case '6':

if ( array_key_exists( 'sub_menu_option', $_REQUEST ) &&

$_REQUEST['sub_menu_option'] == '*' ) {

unset( $_SESSION['first_sub_play_done'] );

$demo->add_action( $c = new phttapi_continue() ); break;

}

$demo->start_variables();

$demo->add_variable( 'main_menu_option', 6 );

$demo->end_variables();



$sub = new phttapi_playback();

$sub->error_file( 'ivr/ivr-that_was_an_invalid_entry.wav' );

$sub->loops( 3 );

$sub->digit_timeout( '15000' );



if ( !array_key_exists( 'first_sub_play_done', $_SESSION ) ) {

$_SESSION['first_sub_play_done'] = TRUE;

$sub->file( 'phrase:demo_ivr_sub_menu' );

} else {

$sub->file( 'phrase:demo_ivr_sub_menu_short' );

}



$star = new phttapi_action_binding( '*' );

$sub->add_binding( $star );



$sub->name( 'sub_menu_option' );



$demo->add_action( $sub ); break;

 

        選項6有點棘手,應該被分解成自己的文件,因爲它在技術上是一個獨立的IVR。爲了方便安裝與測試,我們在這裏把它包含在同一個文件裏。(選項6演示IVR子菜單)

case '9':

$continue = new phttapi_continue();

$demo->add_action( $continue ); break;

 

        這段代碼很簡單,只處理了一個continue,它具有“重複這些選項”的功效,因此它沒有任何綁定,也沒有傳遞main_menu_option參數的方式。

 

default:

$intro = new phttapi_playback();

$intro->error_file( 'ivr/ivr-that_was_an_invalid_entry.wav' );

$intro->loops( 3 );

$intro->digit_timeout( '2000' );

$intro->input_timeout( '10000' );

$intro->name( 'main_menu_option' );

$intro->terminators( '#' );



if ( !array_key_exists( 'first_play_done', $_SESSION ) ) {

$_SESSION['first_play_done'] = TRUE;

$intro->file( 'phrase:demo_ivr_main_menu' );

} else {

$intro->file( 'phrase:demo_ivr_main_menu_short' );

}

 

        默認場景的操作是播放intro文件。爲了模擬IVR 撥號方案APP的工作方式,我們將在會話中存儲一些內容,讓我們知道是否已播放過長的歡迎辭。(關於長短歡迎辭的解釋,請參考第七章的相關內容)

$b1   = new phttapi_action_binding( 1 );

...

$bext = new phttapi_action_binding( '~10[01][0-9]' );

$intro->add_binding( $b1 );

...

$intro->add_binding( $bext );

...

$demo->add_action( $intro );

 

        這一節裏,爲了簡潔起見,用省略號表示省略的其它選項。如你所見,我們爲每個數字選項創建一個綁定對象,然後把每個綁定對象添加到playback action中。然後,與前面示例一樣,我們把action添加到$demo對象中。顯然,我們可以構建出一個更完整的正則表達式,然後只創建一個綁定對象,讓它適配所有的數字。然而,我們的實現方式的最終目的是向你展示:可以使用單個數字和(/或)正則表達式進行多個綁定,並且事情仍然可以按設計工作。

header( 'Content-Type: text/xml' ); print $demo->output();

 

        這裏,我們把應答消息的content type設置爲ext/xml,並打印$demo對象的輸出內容。FreeSWITCH不支持text/xml之外的其它格式,因此,不管你選擇的語言是怎樣配置的,一定要顯式設置這個格式。

        下面是測試時終端的輸出內容:我們首先執行"fsctl loglevel 6"命令,然後發起一路呼叫,接入我們新創建的HTTAPI extension ,其後在撥號盤上按下按鍵“3”:

 

 

總結

        這一章,我們學習了mod_httapi所釋放的新功能。通過web服務器與FreeSWITCH的整合,現在我們能夠用簡單的HTTAPI進行呼叫控制。此外,我們還討論了一個PHP庫(phttapi.php),它提供了一個抽象層,在Web服務器上構建電話應用程序就變得更加容易了。通過mod httapi,企業可以利用其Web開發人員的知識來幫助創建電話應用程序。此外,Web開發人員不需要了解FreeSWITCH管理員需要知道的所有信息。相反,他們可以只學習HTTAPI,就能夠擁有構建功能豐富、網絡控制的電話應用程序所需的一切。

        下一章,我們將聚焦於當今融合世界的一個非常重要的主題:會議與WebRTC會議。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章