ASTERISK 撥號方案基礎

 

摘錄<<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/[email protected])
注意:任何參量爲空,如保留超時參量爲空,如下:
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永遠不要允許撥打去話.(如果一旦給了這樣的機會,外面的人可以撥入你的系統,然後在撥打收費電話出去,讓你來承擔通話費用!)

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