Perl创建VoiceXML应用程序

    为了说明通过电话访问现有的互联网内容是如何简单,我们将使用Perl建立一个简单的CGI脚本文件,获取一个包含CPAN最近上传内容的文件,然后将文件转换为VoiceXML,以便用户能够通过VoiceXML网关在电话上访问这些内容。 
    use strict; 
    use XML::XPath; 
    use LWP::UserAgent; 

    加载必要的模块后,在脚本程序的开始,我们创建新的HTTP::Request和 LWP::UserAgent对象,然后调用LWP::UserAgent的simple_request方法为RSS文件请求远程服务器。 
    my $news_url = 'http://search.cpan.org/recent.rdf'; 
    my $request = HTTP::Request->new('GET', $news_url); 
    my $ua = LWP::UserAgent->new(); 
    my $response = $ua->simple_request($request); 
    发出请求后,我们将开始输出VoiceXML。首先创建vxml root元素和包含一个block元素的表格。在block元素中放入一个audio元素,告诉用户在RSS文件处理过程期间需要耐心地等待,然后用一个goto元素告诉VoiceXML浏览器跳到当前文档中标有headlines的小节。 
    print qq* 

    <vxml> 
    <form id="greeting"> 
    <block> 
    <audio> 
    Please wait while I process the c pan news feed. 
    </audio> 
    <goto next="#headlines"/> 
    </block> 
    </form> 
    *; 
    然后我们将对response对象进行测试,确保我们已经收到了远程的RSS文件。如果已经收到了远程文件,则创建一个新的XML::XPath实例,并将response对象的内容小节传送给它进行解析。如果在请求文件或在解析返回的内容时出现错误,则将出错的信息存储在$error中以供以后分析用。尽管封装对XML::XPath最初调用的eval块增加了一些系统开销,但在解析过程出现错误时,它能够使我们很“体面地”退出程序,如果没有它,解析出现错误将使脚本意外地结束。 
    my ($error, $xp); 

    if ($response->is_success) { 
    eval { 
    $xp = XML::XPath->new(xml => $response->content); 
    $xp->find('/'); 
    }; 
    $error = 'Error parsing RSS file ' . $@ if $@; 

    else { 
    $error = 'Remote server returned ' . $response->message(); 

    如果出现错误,脚本则会向用户返回一个描述错误的audio消息,挂断当前用户的连接,并关闭脚本。 
    if ( defined($error) ) { 
    print qq* 
    <form id="headlines"> 
    <block> 
    <audio> 
    I'm sorry. The following error occurred while fetching 
    the headlines file. $error Please try again later. 
    </audio> 
    <disconnect/> 
    </block> 
    </form> 
    </vxml> 
    *; 


    如果RSS文件的获取和解析过程都没有出现错误,我们将创建一个新的form元素,然后,利用封装在block中的一个audio元素,通知用户解析已经成功,准备收听他所需要的信息。 
    else { 
    print qq* 
    <form id="headlines"> 
    <block> 
    <audio> 
    The RSS file has been fetched and processed successfully. The 
    following modules have recently been up loaded to c pan. 
    </audio> 
    </block> 
    <block> 
    *; 

    然后,我们在一个循环中处理RSS文档中的所有item元素。对于每一个item元素我们都输出一个相应的audio元素,让VoiceXML文档使用每个item元素title子元素的值作为需要输出的文本内容。 
    foreach my $news_item ($xp->findnodes('//item')) { 
    print "<audio>" . 
    $news_item->findvalue('title') . 
    "\n"; 


    最后我们告知用户整个名单已经读完了,并欢迎他明天继续拨打,然后断开连接并关闭VoiceXML文档。 
    print qq* 
    <audio> 
    This completes the latest c pan up loads. Please call again tomorrow. 
    </audio> 
    <disconnect/> 
    </block> 
    </form> 
    </vxml> 
    *; 

    尽管这一段脚本并没有什么实际的用处,但可以考虑一下我们在这一段脚本中完成的任务。在短短的没有几行的脚本中,我们从远程的网站上取出了所需要的资源,并提取出我们关心的内容,并使这些信息可以从全世界的任一部电话上接听。 

    创建动态的VoiceXML应用 

    尽管上面的例子揭示出通过电话提供互联网网站内容和服务的可能性,但用户和VoiceXML应用之间的“对话”还太缺乏交互性。幸运的是,VoiceXML也提供了一些专门用来接收用户输入的元素。另外,象HTML表格那样,VoiceXML表格也可以用来接收通过标准的HTTP GET和POST方法向服务器传送的数据。 
    在下面的例子中,通过创建一个很小的讲述“神奇的oracle”故事的应用程序,我们根据使用POST方法输入的数据创建了一个动态的VoiceXML文档。为了使程序简单明了,我们将把程序分为二部分:一个纯文本的包含获取用户输入表格的VoiceXML文档和动态的、由CGI创建的对这些问题的回答进行规格化的文档。 
    首先,我们来创建这个表格。在表格的开始处是一个问候语,只有用户在第一次连接时才会听到它。 

    <vxml> 
    <form id="greeting"> 
    <block> 
    <audio> 
    Thank you for calling the mystic oracle! 
    </audio> 
    <goto next="#main_query"/> 
    </block> 
    </form> 

    然后,我们将开始设计主表格。这个表格只包含一个名字为query_type的字段,它的用途是获取用户的问题。我们需要特别注意grammar元素,它使VoiceXML应用开发人员能够精确地确定给定字段能够接收什么类型的输入。例如,在我们的例子中,如果用户的问题中含有“career”、“job”、“boss”、“coworker”或“department”等词汇,query_type字段的值勤将被设置为“career”。 
    <form id="main_query"> 
    <field name="query_type"> 
    <grammar type="application/x-gsl" name="qtype"> 
    <![CDATA[ 

    [romance love sex boyfriend girlfriend] {} 
    [career job boss coworker department] {} 
    [family husband wife mother father son daughter] {} 

    ]]> 
    </grammar> 

    prompt元素通知VoiceXML网关向用户朗读一段文字,并等待响应。在本例中,如果用户提问的问题中包含上面一段脚本中的grammar元素中定义的词汇之一,应用程序就会向用户表示感谢,并使用POST方法把信息提交给应用程序的CGI部分。submit元素的namelist属性允许我们指定准备提交当前文档中的哪个字段或变量。 
    <prompt> 
    Clear your mind and concentrate on your question.  
    You may ask your question now. 
    </prompt> 
    <filled> 
    <audio>thank you.</audio> 
    <submit next="http://mysite.tld/cgi-bin/mystic_response.cgi
    method="POST" 
    namelist="query_type"/> 
    </filled> 

    下面的代码是一个错误陷阱结构,可以使oracle应用有一个更合理的界面。如果用户所问的问题中不包含上面的grammar元素中所包含的词汇,nomatch标记中所包含的文本就会被朗读出来。reprompt元素使VoiceXML浏览器返回到所在模块的开始,并重新朗读前面的提示。如果用户没有提问问题,系统将根据每个noinput的count属性定义的顺序每次朗读一个noinput元素。如果三次提示后用户仍然没有提问问题,应用程序将挂断与用户的连接。 
    <nomatch> 
    The mystic oracle can only answer questions about romance, career, 
    or family matters. Please try again. 
    <reprompt/> 
    </nomatch> 
    <noinput count="1"> 
    I can sense your apprehension. 
    <reprompt/> 
    </noinput> 
    <noinput count="2"> 
    You must say something. 
    <reprompt/> 
    </noinput> 
    <noinput count="3"> 
    Please call back when you are less stressed. 
    <disconnect/> 
    </noinput> 
    下面的内容是为了结束query_type字段、其父表格和最顶部的vxml元素。 
    </field> 
    </form> 
    </vxml> 

    下面我们来创建响应用户问题的CGI脚本程序。这一脚本程序将接收一个通过POST方法传送、名字为query_type的字段的参数,query_type的可能的值为romance、career或 family。 

    use strict; 
    use CGI qw(:standard); 

    my $q = CGI->new(); 

    my $query_type = $q->param('query_type'); 

    首先,我们将生成一个针对用户所提问题的比较含糊的答案。 
    my @intro_phrases = ('My sources say', 
    'All signs indicate that', 
    'Search your heart. You know'); 

    my @responses = ('the answer is yes.', 
    'the answer is no.', 
    'it is too soon to tell.', 
    'the outlook is hazy. Please ask again later.'); 

    my $response_text = $intro_phrases[int( rand ( scalar (@intro_phrases) ) )] . 
    ' ' . 
    $responses[int( rand ( scalar (@responses) ) )]; 

    Then we will create the VoiceXML output. Note how the inline Perl variables are used both to include the randomly-generated answer to the caller's question ($response_text) and to create an illusory sense of personalized context by repeating the general type of question that the caller asked ($query_type). 
    下面我们将创建VoiceXML的输出。请注意内置的Perl变量━━$response_text和$query_type的使用。 
    print qq* 

    <vxml> 
    <form id="response"> 
    <block> 
    <audio> 
    I sense your consternation. 
    Questions about $query_type can be very troublesome, indeed. 
    $response_text 
    </audio> 
    <goto next="#new_or_exit"/> 
    </block> 
    </form> 

    作为最后一个功能,我们将让用户有机会向mystic oracle提另一个问题。如果在得到系统的提示后,用户回答“no”,系统将会礼貌地谢谢用户并挂断连接,否则系统将返回到上面文档中的main小节,用户将听到提出新问题的提示。 

    <form id="new_or_exit"> 
    <field name="new_question"> 
    <prompt> 
    Would you like to ask another question? 
    </prompt> 
    <grammar> 
    <![CDATA[ 

    [yes] {} 
    [no] {} 

    ]]> 
    </grammar> 
    <filled> 
    <if cond="new_question=='no'"> 
    <audio>Thank you for calling. Goodbye.</audio> 
    <disconnect/> 
    <else/> 
    <goto next="http://mysite.tld/vxml/mystic_prompt.xml#main_query"/
    </if> 
    </filled> 
    </field> 
    </form> 
    </vxml> 
    *; 

    用户和mystic oracle之间的典型的对话可能如下所示: 

    Oracle: Thank you for calling the mystic oracle! 
    Clear your mind and concentrate on your question. 
    You may ask your question now. 

    Caller: Hmmmm. Should I take the new job I was just offered? 

    Oracle: Thank you. 
    I sense your consternation. 
    Questions about career can be very troublesome, indeed. 
    All signs indicate that the answer is yes. 
    Would you like to ask another question? 

    Caller: Yes, I would. 

    Oracle: Clear your mind and concentrate on your question. 
    ... 

posted @ 2010-08-17 19:32  l2010q2010  阅读(894)  评论(0编辑  收藏  举报