概述
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将执行指令,然后再次向webserver发HTTP请求。如此循环,直到话务被挂断或转移。
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/1010@52.57.248.151 -->
<!-- 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:1007@192.168.1.101">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:2019@10.1.1.12
</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="1007@192.168.1.101"
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。示例配置中包含一个名字叫default的profile (你可以建立自己的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(1010@192.168.1.100)}
这行代码将以给定的参数执行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/1010@192.168.1.100)
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会议。
最后
以上就是怡然星月为你收集整理的第十二章 HTTAPI – FreeSWITCH向Webserver寻问下一步操作HTTAPI原理HTTAPI拨号方案HTTAPI文档语法mod_httapi配置文件PHP和Python的HTTPAPI开发库PHP-HTTAPI版的演示IVR总结的全部内容,希望文章能够帮你解决第十二章 HTTAPI – FreeSWITCH向Webserver寻问下一步操作HTTAPI原理HTTAPI拨号方案HTTAPI文档语法mod_httapi配置文件PHP和Python的HTTPAPI开发库PHP-HTTAPI版的演示IVR总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复