第十二章 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会议。

猜你喜欢

转载自blog.csdn.net/yetyongjin/article/details/102407424