您的位置:首页 > 其它

Asterisk 拨号方案一20121106

2012-11-06 10:14 218 查看
摘录<<Asterisk.The.Future.of.Telephony>>
 
拨号方案定义了Asterisk如何处理来话和去话,它由指令和步骤列表组成,Asterisk按步骤来执行这些指令.与传统电话系统不同,Asterisk的拨号方案是完全可定制的.
5.1 拨号方案语法

拨号方案在文件extensions.conf中定义.文件extensions.conf通常在/etc/asterisk/目录下.但是其位置可以改变,取决于Asterisk的安装方式.其它常见的位置包括/usr/local/asterisk/etc/和/opt/asterisk/etc/.

拨号方案由4部分组成:contexts,extensions,priorities和applications.
Context

Dialplans被分成几个段,这些段称为context.Context用来对extension的组命名.在一个context中定义的extension完全独立于另一个context中定义的extensiont,本章末尾将介绍如何允许context之间相互交织.

Contexts的表示方法是把名字放在方括号([])的中间.这个名字可以由A-Z,数字0-9,以及连字号和下划线组成.如: [incoming]
Extension

在每一个context内,可以定义一个或者多个extension. extension是Asterisk要执行的指令,由来电或者通道上所拨数字来触发.

extension的语法是: exten=>

之后是extension的名字.在于电话系统打交道的时候,我们把extension看作是呼叫另一部电话所拨的号码.在asterisk上,意味着更多的东西.

一个完整的extension由三部分组成:

Extension的名字或者号码

Priority(每个extension可以有多个步骤,步骤的编号称作Priority)

应用(或者命令),针对呼叫完成一些动作

这三个部分用英文逗号分开,如:

exten=>name,priority,application()

例如:

exten=>123,1,Answer()

在这个例子中,extension的名字是123, priority是1, 应用是Answer().
Priority

每个extension可以有多个步骤,称作priorities. 每个priority都按顺序编号,从1开始.每个priority执行一个规定的应用.下面给出一个例子,这个extension接听电话(编号为1的priority),然后挂断(编号为2的priority):

exten=>123,1,Answer()

exten=>123,2,Hangup()

注意:必须确保priority从1开始并且是连续的编号.对于特定的extension,Asterisk遵从priority的数字顺序.

Application
无序号的priority

在Asterisk1.2版本里,Priority编号增加了新的变化,引入了n priority,表示"下一个"的意思.每次Asterisk遇n这个priority的时候,就取出前一个priority的编号加上1.例如:

extern=>123,1,Answer()

extern=>123,n,do something

extern=>123,n,do something else

extern=>123,n,do one last thing

extern=>123,n,Hangup()
1.2版也允许给priority分配一个文字标号.要给priority分配文字标号,只需要在priority后面的括号内加上这个标号,如:

extern=>123,n(label),do something

5.2 一个简单的拨号方案

我们以一个简单的例子来开始.在呼叫进来时,Asterisk应答这个呼叫,播放声音文件,然后挂断,用这个简单例子来指出拨号方案基本原理.

为了工作正确,至少创建一个通道(Zap或Sip通道),并且作了配置.
s Extension

在着手拨号方案前,先介绍一个特别的extension,名字叫做s. 当没有指定extension的呼叫(例如,正在振铃的FXO线路)进入context的时候,就由这个s extension来处理.(s表示"start"开始,因为多数的呼叫都是从s extension开始).我们要在呼叫上完成三个动作(接听,播放声音文件,最后挂断),所以要创建有三个priority的s extension. 我们把这三个priority放到[incoming]里,所有来话都应该从这个context开始:

[incoming]

exten=>s,1,application()

exten=>s,2,application()

exten=>s,3,application()

现在我们所要做的事情就是填写应用.
Answer(),Playback()和Hangup()应用
Answer()应用用于接听正在振铃的通道,并不为接受来话的通道进行初始化设定(确有不少应用不需要先应答通道,但是在完成其它动作之前进行适当的应答是一个好习惯).Answer()不需要任何参量.
Playback()应用用于在通道上播放事先录制好的语音文件.在使用Playback()应用时,系统不会理会来自用户的输入.Asterisk带有很多专业录制的语音文件,默认的目录通常是/var/lib/asterisk/sounds/. 这些文件都是GSM格式.使用格式如下:

Playback(filename) ;将播放文件名为filename.gsm的语音文件,并假定这个文件位于默认的语音文件目录内.

Playback(/home/tim/sounds/filename) ;也可以包括完整路径,将播放/home/tim/sounds/目录下的filename.gsm文件.

Playback(custom/filename) ;将播放默认语音文件目录内custom/子目录中的filename.gsm文件.
Hangup()完成挂断一个正在活动的通道.主叫方将收到通话挂断的指示.当要结束通话时,可以在context的末尾使用这个应用,以确保主叫不会继续停留在拨号方案内.这个应用不需要参量.
第一个拨号方案

方案如下:

[incoming]

exten=>s,1,Answer()

exten=>s,2,Playback(hello-world)

exten=>s,3,Hangup()
5.3 在拨号方案中加入逻辑

刚刚建立的拨号方案是静态的,对每个呼叫总是作相同的事情,现在加入一些逻辑,让它根据用户的输入来完成一些不同的动作.
Background()和Goto()应用
构建交互式Asterisk系统的关键是Background()应用.与Playback()相同的是,它也播放事先录制好的语音文件;与Playback()不同的是,当主叫方按下电话键(1个或者多个)的时候,会中断语音的播放,转到与所按数字对应的extension. 例如,假设主叫方按下5, Asterisk停止播放语音,把呼叫的控制发送给extension 5的第一个priority.Background()语法与Playback()类似.

Background()应用通常用于创建语音菜单,以免接待员接听每一个电话
另外一个有用的应用是Goto().它用于把呼叫发送到另一个context,extension,以及priority. Goto应用使得在拨号方案的不同部分有序的转移非常容易.语法如下:

exten=>123,1,Goto(context,extension,priority)
看下面例子:

[incoming]

exten=>s,1,Answer()

exten=>s,2,Background(enter-ext-of-person)

exten=>1,1,Playback(digits/1)

exten=>1,2,Goto(incoming,s,1)

exten=>2,1,Playback(digits/1)

exten=>2,2,Goto(incoming,s,1)
非法输入和超时的处理

第一个语音菜单已经完成,我们在加入几个附加的特殊extension.首先,需要一个用来处理非法输入的extension,从而在主叫方按下一个无效输入(比如在上面的例子中按下3),呼叫被送到 i extension. 其次,需要一个extension来处理主叫方没有及时(默认的时间是10秒)输入的情况.如果主叫方在Background()完成语音文件播放之后很久才按键,呼叫将被转移到 t extension. 如下:

[incoming]

exten=>s,1,Answer()

exten=>s,2,Background(enter-ext-of-person)

exten=>1,1,Playback(digits/1)

exten=>1,2,Goto(incoming,s,1)

exten=>2,1,Playback(digits/2)

exten=>2,2,Goto(incoming,s,1)

exten=>i,1,Playback(pbx-invalid)

exten=>i,2,Goto(incoming,s,1)

exten=>t,1,Playback(vm-goodbye)

exten=>t,2,Hangup()

现在功能还是有限,因为外面的主叫无法与实际的人联系.为了做到这一点,我们还需要一个Dial()应用.
使用Dial()应用

Dial()需要4个参量.第1个是呼叫的被叫地,由呼叫所采用的(传输)技术,反斜线,远地资源(通常是通道名称或编号)等组成.例如,假定我们要呼叫名字为Zap/1的Zap通道(连接了普通模拟电话的FXS通道),那么技术是Zap,资源是1. 与此类似,到一个SIP设备的呼叫的被叫地是SIP/1234, 而到一个IAX设备的呼叫的被叫地是IAX/fred. 假如在extension123到达拨号方案时,要Asterisk对Zap/1通道振铃,要加入下面这个extension:

exten=>123,1,Dial(Zap/1)

当这个extension被执行时,Asterisk会连接通道上Zap/1的电话振铃,也可以同时拨多个通道,如下:

exten=>123,1,Dial(Zap/1&Zap/2&Zap/3)

Dial()会桥连来电,无论被叫地中的哪一个通道先接听.

Dial()应用的第2个参量是超时,单位为秒.如果给定了超时参量,Dial()会一直对被叫地进行呼叫,直到超时后才放弃,然后转移到该extension中的下一个priority. 如果没有指定超时时间,Dial()会一直呼叫该通道,直到有人接听,或者主叫挂机.我们把10秒超时加到extension中:

exten=>123,1,Dial(Zap/1,10)

如果呼叫在超时之前被接听,通道就被桥连,拨号方案完成,如果被叫地没有应答,Dial()会继续到该extension的下一个priority.但是如果被叫通道忙,Dial()将转到priority n+101, 如果其存在的话(其中的n是Dial()被调用的priority).这样我们就能够以不同于被叫地忙的方式来处理未接听电话.如下:

exten=>123,1,Dial(Zap/1,10)

exten=>123,2,Playback(vm-nobodyavail)

exten=>123,3,Hangup()

exten=>123,102,Playback(tt-albusy)

exten=>123,103,Hangup()

正如你所看到的,在这个例子中,如果呼叫未被接听,将播放vm-nobodyavail.gsm语音文件,如果Zap/1通道正忙,则播放tt-allbusy.gsm语音文件.
Dial()应用的第3个参量是可选择的字符串.它包含一个或多个能够影响Dial()应用行为的字符.最常用的是字母r.如果把r作为第3个参量,在通知被叫通道有来电的这段时间内,主叫方会听到振铃声音.

此时,已经知道如何使用Dial()应用,拨号方案中编号为1和2的extension变得没有用了.我们把它们用extension101和102来代替,这样就允许外部的主叫方把电话打给tim和david:

[incoming]

exten=>s,1,Answer()

exten=>s,2,Background(enter-ext-of-person)

exten=>101,1,Dial(Zap/1,10)

exten=>101,2,Playback(vm-nobodyavail)

exten=>101,3,Hangup()

exten=>101,102,Playback(tt-allbusy)

exten=>101,103,Hangup()

exten=>102,1,Dial(SIP/david,10)

exten=>102,2,Playback(vm-nobodyavail)

exten=>102,3,Hangup()

exten=>102,102,Playback(tt-allbusy)

exten=>102,103,Hangup()

exten=>i,1,Playback(pbx-invalid)

exten=>i,2,Goto(incoming,s,1)

exten=>t,1,Playback(vm-goodbye)

exten=>t,2,Hangup()
Dial()应用的最后一个参量是URL.如果被叫通道支持在呼叫的同时接受URL,那么所指定的URL将被发送,这个参量很少使用.

如果要在FXO Zap通道上产生一个去话,可以使用下面的语法在那个通道上拨号:

exten=>123,1,Dial(Zap/4/5551212)

这个例子在Zap/4通道上拨号码555-1212.对于其它类型的通道,如SIP和IAX,简单的把被叫地作为资源,见下:

exten=>123,1,Dial(SIP/1234)

exten=>124,1,Dial(IAX2/tim@asteriskdocs.org)

注意:任何参量为空,如保留超时参量为空,如下:

exten=>123,1,Dial(Zap/1,,r)

给内部呼叫增加Context

context的一个重要功能是为不同的主叫用户区分特权(如长途通话,或者呼叫特定的extension).在下面例子中,我们建立两个内部电话的extension,加到拨号方案里去,然后配置这两个extension可以彼此呼叫.为达到这个目的,创建一个新的叫做[internal]的context.
[incoming]

exten=>s,1,Answer()

exten=>s,2,Background(enter-ext-of-person)

exten=>101,1,Dial(Zap/1,10)

exten=>101,2,Playback(vm-nobodyavail)

exten=>101,3,Hangup()

exten=>101,102,Playback(tt-allbusy)

exten=>101,103,Hangup()

exten=>102,1,Dial(SIP/david,10)

exten=>102,2,Playback(vm-nobodyavail)

exten=>102,3,Hangup()

exten=>102,102,Playback(tt-allbusy)

exten=>102,103,Hangup()

exten=>i,1,Playback(pbx-invalid)

exten=>i,2,Goto(incoming,s,1)

exten=>t,1,Playback(vm-goodbye)

exten=>t,2,Hangup()
[internal]

exten=>101,1,Dial(Zap/1,,r)

exten=>102,1,Dial(SIP/david,,r)
在这个例子中,我们在[interanl]context中加了两个新的extension.这样,使用通道Zap/1的人可以拿起电话拨102来拨打通道channel SIP/david上的人,同样,注册为SIP/david的电话可以拨打101来拨打Zap/1的电话.

如果你认为你的用户可能通过支持名字的VoIP来传输拨号,添加使用名字的extension也不是什么不好的事情.如下修改:

[internal]

exten=>101,1,Dial(Zap/1,,r)

exten=>tim,1,Dial(Zap/1,,r)

exten=>102,1,Dial(SIP/david,,r)

exten=>david,1,Dial(SIP/david,,r)
使用变量

例如,建立一个叫做TIM的变量,并给其赋予值Zap/1.这样,在写拨号方案时,可以利用名字来引用Tim的通道,不需要直接记住Tim使用的Zap/1.为了把指赋给变量,只需要输入变量名称,等号和值,如下所示:

TIM=Zap/1

引用变量有两种方法.若要引用变量的名字,仅仅需要输入变量的名字就可以了,例如:TIM;但如果要引用变量的值,则必须输入美元符号,紧接着是大括号,在大括号内输入变量名.下面的例子说明了如何在应用中引用变量:

exten=>555,1,Dial(${TIM},,r)
在拨号方案中有三种变量可以使用:全局变量,通道变量和环境变量.

1.全局变量

全局变量适用于所有context里的所有extensions.全局变量的好用之处在于它可以用于拨号方案中的任何地方,能够增加可读性和可管理性.

全局变量应该在extensions.conf文件的开始利用[globals]context定义.也可以使用编程的方式定义,利用SetGlobalVar()应用.如下:

[globals]

TIM=Zap/1



[internal]

exten=>123,1,SetGlobalVar(TIM=Zap/1)
2.通道变量

通道变量与特定的呼叫相关的变量(如Caller*IDnumber),与全局变量不同,通道变量只能在当前呼叫存在期间定义,并只能用于参与该呼叫的通道.

有很多的预先定义的通道变量可以用于拨号方案,在Asterisk源程序的doc子目录下README文件中有详细的说明.通道变量使用Set()应用来设置:

exten=>123,1,Set(MAGICNUMBER=42)
3.环境变量

环境变量是一种在Asterisk中访问操作系统环境变量的方法.这些变量以${ENV(var)}形式引用,其中的var是所要引用的操作系统环境变量.
4.在拨号方案中加入变量

我们为两个人,Tim和David加入变量:

[globals]

TIM=SIP/tim

DAVID=Zap/1
[incoming]

exten=>s,1,Answer()

exten=>s,2,Background(enter-ext-of-person)

exten=>101,1,Dial(${TIM},10)

exten=>101,2,Playback(vm-nobodyavail)

exten=>101,3,Hangup()

exten=>101,102,Playback(tt-allbusy)

exten=>101,103,Hangup()

exten=>102,1,Dial(${DAVID},10)

exten=>102,2,Playback(vm-nobodyavail)

exten=>102,3,Hangup()

exten=>102,102,Playback(tt-allbusy)

exten=>102,103,Hangup()

exten=>i,1,Playback(pbx-invalid)

exten=>i,2,Goto(incoming,s,1)

exten=>t,1,Playback(vm-goodbye)

exten=>t,2,Hangup()
[internal]

exten=>101,1,Dial(${TIM},,r)

exten=>102,1,Dial(${DAVID},,r)

模式匹配

模式匹配可以使用一段代码来对应许多不同的extensions.
1.模式匹配语法

使用模式匹配的时候,用不同的字母和符号来代表肯恩个要匹配的数字.模式总是用一个下划线(_)开始,它告诉Asterisk要做模式匹配,这不是一个extension名字.(这意味着不能用下划线作为extension名字的开始字符)

在下划线之后,可以使用一个或者多个下面列出的字符:

X

匹配0-9的任何数字.

Z

匹配1-9的任何数字

N

匹配2-9的任何数字

[15-7]

匹配任何数字或者指定的数字范围.在这个例子中,匹配1,5,6或7.

.(句号)

通配符,匹配一个或多个字符.

应该在匹配了其他数字之后在使用通配符,如:

_.

实际上,如果你试图使用它,Asterisk会警告你.如果可能,尽量使用下面的模式:

_X.
若要在拨号方案中使用模式匹配,只要把模式放在extension名字的位置:

exten=>_NXX,1,Playback(auth-thankyou)

在这个例子中,模式会匹配3位的extension,从200到999,这就是说,在这个context中,如果主叫拨200-999之间的任何extension,都会听到声音文件auth-thankyou.gsm的声音.

必须了解的一件事情是,如果Asterisk发现有多个模式与所拨的extension匹配,它会使用最接近的那一个模式.比如说定义了下面的两个模式,主叫方拨的号码是888-555-1212:

exten=>_555XXXX,1,Playback(digits/1)

exten=>_55512XX,1,Playback(digits/2)

在这个例子中,会选择第2个extension,因为它更接近.
2.模式匹配的实例

NANP与话费欺诈


3.使用${EXTEN}通道变量

一旦拨了某个extension,Asterisk会把通道变量${EXTEN}设置为所拨的数字.可以使用应用SayDigits()来检测出来:

exten=>_XXX,1,SayDigits(${EXTEN})

在这个例子中,SayDigits()应用会把所拨的3位extension读出来.

通常,把extension的前面几位去掉对于处理${EXTEN}是很有用的.可以利用这样的语法来实现: ${EXTEN:x},其中x是要去掉的位数.例如,假设EXTEN的值是95551212,那么${EXTEN:1}等于5551212. 再来看另外一个例子:

exten=>_XXX,1,SayDigits(${EXTEN:1})

在这个例子中,SayDigits()应用把所拨的extension的最后两位读出来.如果x是负数,SayDigits()给出所拨的extension的最后x位.在下面的例子中,SayDigits()只读出所拨的extension的最后1位:

exten=>_XXX,1,SayDigits(${EXTEN:-1})

开启去话拨号

允许用户向外拨打电话,首先要做的一件事情是给[globals]context加一个变量,用于定义那一个通道可以用来向外拨打电话:

[globals]

TIM=SIP/tim

DAVID=Zap/1

OUTBOUNDTRUNK=Zap/4

接下来,在拨号方案中添加一个用于去话的context.使用一个单独的context的目的是能够规定和控制谁可以拨打电话,以及可以拨打什么样的去话.

首先,建立一个用于本地电话的context.为了与传统电话交换机保持一致,我们在模式之前放上9,因此用户必须在呼叫外部号码之前拨9:

[outbound-local]

exten=>_9NXXXXXX,1,Dial(${OUTBOUNDTRUNK}/${EXTEN:1})

exten=>_9NXXXXXX,2,Congestion()

exten=>_9NXXXXXX,102,Congestion()
注意: 拨9并没有立即给你外线,这和传统的PBX系统不同.一旦在FXS线路上拨9,拨号音会停止.如果在拨9之后还希望有拨号音,加入下面一行(就在context定义之后):

ignorepat=>9

这个指令告诉Asterisk继续提供拨号音,即便是在主叫方已经拨了指示的模式.

回顾一下我们刚刚所做的事情.增加了一个全局变量OUTBOUNDTRUNK,它会控制使用哪一个通道用于去话,还增加了一个用于本地去话的context.在priority1中,取出所拨的extension,用${EXTEN:1}语法去掉9,然后试图在变量OUTBOUNDTRUNK所指定的通道上拨这个号码.如果呼叫成功,主叫方就与去话通道建立桥连.如果呼叫不成功(要么是通道忙,要么是因为某种原因不能拨这个号码,调用Congestion()应用,播放"快忙音"(拥挤声音)让主叫方知道呼叫不成功.

在进一步往下走之前,先确认一下这个拨号方案允许拨打紧急电话号码:

[outbound-local]

exten=>_9NXXXXXX,1,Dial(${OUTBOUNDTRUNK}/${EXTEN:1})

exten=>_9NXXXXXX,2,Congestion()

exten=>_9NXXXXXX,102,Congestion()
exten=>911,1,Dial(${OUTBOUNDTRUNK}/911)

exten=>9911,1,Dial(${OUTBOUNDTRUNK}/911)

下面,给拨号方案加一个用于长途电话的context:

[outbound-long-distance]

exten=>_9NXXXXXX,1,Dial(${OUTBOUNDTRUNK}/${EXTEN:1})

exten=>_91NXXNXXXXXX,2,Congestion()

exten=>_91NXXNXXXXXX,102,Congestion()

现在有了两个新的context,如何允许内部用户利用它们?我们需要一种办法来使得一个context能够使用另一个context.
INCLUDES

Asterisk允许在一个context中使用另一个context,通过include指令来实现.这用来授予访问给不同的拨号方案段.我们使用include功能来让[internal]context中的用户能够拨打去话.首先介绍一下语法.

include语句的形式如下所示,其中的是我们要包含在当前context的远地context:

include=>context

在当前context包含另外的context时,必须注意包含的顺序.Asterisk首先试图在当前context中匹配extension.如果不成功,会去尝试第一个包含进来的context,然后按照包含顺序再去尝试其他的context.

到目前为止,拨号方案有两个context用于去话.但是[internal]context中的人还不能够使用它们.我们用在[internal]context包含两个去话context来实现使用,如下所示:

[globals]

TIM=SIP/tim

DAVID=Zap/1

OUTBOUNDTRUNK=Zap/4
[incoming]

exten=>s,1,Answer()

exten=>s,2,Background(enter-ext-of-person)

exten=>101,1,Dial(${TIM},10)

exten=>101,2,Playback(vm-nobodyavail)

exten=>101,3,Hangup()

exten=>101,102,Playback(tt-allbusy)

exten=>101,103,Hangup()

exten=>102,1,Dial(${DAVID},10)

exten=>102,2,Playback(vm-nobodyavail)

exten=>102,3,Hangup()

exten=>102,102,Playback(tt-allbusy)

exten=>102,103,Hangup()

exten=>i,1,Playback(pbx-invalid)

exten=>i,2,Goto(incoming,s,1)

exten=>t,1,Playback(vm-goodbye)

exten=>t,2,Hangup()
[internal]

include=>outbound-local

include=>outbound-long-distance
exten=>101,1,Dial(${TIM},,r)

exten=>102,1,Dial(${DAVID},,r)
[outbound-local]

exten=>_9NXXXXXX,1,Dial(${OUTBOUNDTRUNK}/${EXTEN:1})

exten=>_9NXXXXXX,2,Congestion()

exten=>_9NXXXXXX,102,Congestion()
exten=>911,1,Dial(${OUTBOUNDTRUNK}/911)

exten=>9911,1,Dial(${OUTBOUNDTRUNK}/911)
[outbound-long-distance]

exten=>_9NXXXXXX,1,Dial(${OUTBOUNDTRUNK}/${EXTEN:1})

exten=>_91NXXNXXXXXX,2,Congestion()

exten=>_91NXXNXXXXXX,102,Congestion()

这两个include语句让[internal]context内的主叫方可以拨打去话.应该注意到,出于安全的考虑,要确保[inbound]context永远不要允许拨打去话.(如果一旦给了这样的机会,外面的人可以拨入你的系统,然后在拨打收费电话出去,让你来承担通话费用!)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: