Asterisk検証めも
検証が進むにつれ追記していきます。たぶん。
環境
OS | Asterisk | AGI Library | Python |
---|---|---|---|
CentOS 7.3 | Asterisk 15.3 | pyst2 | Python 3.4 |
pyst2
https://github.com/rdegges/pyst2
構築
事前準備
必要パッケージインストール
# yum update
# yum install make wget openssl-devel ncurses-devel newt-devel libxml2-devel kernel-devel gcc gcc-c++ sqlite-devel libuuid-devel json-glib json-glib.i686 json-glib-devel.x86_64 jansson-devel.x86_64
# reboot
公式から以下のファイルをダウンロード
※今回はアナログ回線を接続しないのでdadhiとlibpriは不要
asterisk-15-current.tar.gz
Githubから以下をダウンロード(pjsip)
https://raw.githubusercontent.com/asterisk/third-party/master/pjproject/2.7.1/pjproject-2.7.1.tar.bz2
https://raw.githubusercontent.com/asterisk/third-party/master/pjproject/2.7.1/MD5SUM.TXT
// 解凍
# tar zxvf asterisk-15-current.tar.gz
// 本体配置
# mv asterisk-15.3.0 /usr/local/src/
// ローカルインストール用ディレクトリ作成
# mkdir /tmp/downloads
// pjsip 配置
# mv pjproject-2.7.1.tar.bz2 /tmp/downloads/
# mv MD5SUM.TXT /tmp/downloads/pjproject-2.7.1.md5
// fax用モジュールインストール
# tar zxvf spandsp-20180108.tar.gz
# cd spandsp-0.0.6
# ./configure
# make
# make install
//サウンドファイル配置 (使いたいファイルだけセットで落としてくる)
# mv asterisk-core-sounds-en-wav-1.6.1.tar.gz /tmp/downloads/
# mv asterisk-core-sounds-en-wav-1.6.1.tar.gz.sha1 /tmp/downloads/
インストール
// Asteriskインストール
# cd /usr/local/src/asterisk-15.3.0
# ./configure --with-download-cache=/tmp/downloads
# make menuselect
※他に必要なモジュールがあればチェックを付ける(mp3やFax等)
// faxモジュール
[*] res_fax
[*] res_fax_spandsp
// mp3モジュール
[*] format_mp3
# make
# make install
# make samples
# make config
# systemctl start asterisk
# systemctl status asterisk
# systemctl enable asterisk
設定
基本的にはインストールで実施した手順でサンプルのコンフィグファイルが生成されているはずですので、
該当部分をコメントアウトすればOKだと思います。
API設定
AsteriskのRestfulAPIをオン
enabled=yes
pretty=yes
[asterisk]
type=user
read_only=no
password=パスワード
[general]
enable=yes
bindaddr=0.0.0.0
bindport=8088
言語設定(任意、デフォルトの音声データ参照先ディレクトリが変わります)
defaultlanguage=ja
###ログ出力設定
デフォルトだといくつかログ出力のレベルが低いので設定します
console => notice,warning,error,debug,dtmf,verbose,security,fax
messages => notice,warning,error,debug,dtmf,verbose,security,fax
外線設定(楽天コミュニケーションズ IP電話 B2BUA OpenGate サービス)
今回楽コムのOpenGateサービスにしたのは主に以下の理由です。
- 支払方法・費用
- Asteriskの動作実績
1についてですが、これはうちの会社だと開発の部署で法人名義のクレカが使えなかったためです。
そのため第一候補に挙がっていたtwilioに請求書払いが可能かどうか問い合わせたところ、
書類手続きに月5000円かかるとのことなのでこちらになりました。
第2候補ではありましたが、細かい質問に対しても割とレスポンスが早く、開発部署と連携して回答頂いたので個人的に楽コムは好印象でした。
ただ法人としての契約の場合、実印や印鑑証明書や履歴事項全部証明書を求められるので・・・・
所属しているのが大企業の場合は素直にTwilioをおすすめします。
(手続きや申請が苦にならないならどちらでも良いと思いますが)
[general]
maxexpirey=3600
defaultexpirey=3600
srvlookup=no
allowguest=no
port=5060
bindaddr=aaaa.aaaa.aaaa.aaaa
transport=udp
rtptimeout=60
rtpholdtimeout=300
session-timers=refuse
session-expires=3600
session-refresher=uas
localnet=xxxx.xxxx.xxxx.xxxx/255.255.255.255(サブネット)
externaddr=yyyy.yyyy.yyyy.yyyy
register=ユーザID:パスワード@sipプロキシ/test(設定名)
[test]
type=frined
username=ユーザID
fromuser=ユーザID
secret=パスワード
host=sipプロキシ
fromdomain=okj.sip.0038.net
insecure=port,invite
context=コンテキスト名
※セッション周りの設定はいらないかも・・・
発信
Asteriskでの発信方法は概ね以下の3つになる。
- call fileの作成
- channels API(ARI)の利用
- SIPクライアントからの直接発信
ARI と AGI
AsteriskでのREST APIは **ARI (Asterisk REST Interface)**という名前でまとめられていて、
逆にローカルで直接Asteriskを操作するようなインターフェースは
**AGI (Asterisk Gateway Interface)**を利用します。
ARI
https://wiki.asterisk.org/wiki/pages/viewpage.action?pageId=29395573
AGI
https://wiki.asterisk.org/wiki/pages/viewpage.action?pageId=32375589
channles API
まず今回主に利用するchannles APIですが、書式は以下です。
API
method | URI | optional |
---|---|---|
POST | /channels/{channle_id} | channel_id |
パラメータ
param | required | type | description |
---|---|---|---|
endpoint | true | string | 宛先 |
extension | exclusive | string | extensions.confのextension |
context | exclusive | string | extensions.confのconext |
callerId | false | string | 発信元 |
timeout | false | int | タイムアウト(sec) |
その他のオプションは今回あまり使わないので知りたい場合はリファレンスを参照してください
API経由での発信
SIPクライアント同士がAsteriskを経由して通話を行うのは比較的簡単です。
しかしAsteriskのAPIをトリガーに複雑な制御を行った発信を行うにはいくつか注意が必要です。
例えば、APIリクエスト時には発信先を指定せずリクエストに載せた追加パラメータに沿って複数に発信したい場合などです。
何故ならAsteriskでは実在する(レジストされた)SIPデバイスしかendpointに指定出来ないので、
リクエスト時に発信先を必ず指定する必要があるからです。
つまりこの方法でやるとなると、通常は代表番号のようなものを用意して必ず同じ番号を1つendpointに設定することになります。
ローカルチャンネル
上記の問題を解決するのがローカルチャンネルです。
ローカルチャンネルはAsterisk内部に仮想endpointを生成し、そこを経由することにより実デバイス同士が通信しているように見せかけます。
Asterisk ====> 仮想エンドポイント ====> ブリッジ ====> 宛先
以下のようにextension.confを設定し、リクエストに所定のパラメータを設定するとtalkコンテキストに飛んで「ハロー」という音声が流れるはずです。
あとはcallコンテキストの部分をスクリプトで置き換え、やりたいことに合わせてスクリプトで発信の制御を行えば順番に発信したり一斉に発信したりすることが可能です。
[call]
exten => s, 1, Call(SIP/111)
[talk]
exten => s, 1, Answer()
exten => s, n, Playback(hello)
exten => s, n, Hangup()
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
import requests
import json
class ARI:
def __init__(self, *, user='asterisk', password='asterisk', ip_addr='localhost', port='8088'):
self.user = user
self.password = password
self.port = port
self.base_url = 'http://{user}:{password}@{ip_addr}:{port}/ari/'.format(
user=user, password=password, ip_addr=ip_addr, port=port
)
def call(self, params, data, headers):
api = "channels"
url = self.base_url + api
print("URL:" + url)
print(json.dumps(data))
response = requests.post(url, params=params, data=json.dumps(data), headers=headers)
return response
def main():
asterisk = ARI(user="asterisk", password="asterisk")
headers = {'content-type':'application/json'}
call_params = {
"endpoint":"Local/s@call/n",
"extension":"s",
"context":"talk",
"priority":"1",
"timeout":"10",
}
data = {"variables": {"call_list":"[111,222]"}}
print(asterisk.call(call_params, data, headers).text)
if __name__ == '__main__':
main()
CallFileでの発信
発信時のステータス遷移
発信時にはAsterisk内部でchannelが生成され、クライアント側・サーバ側の動作によりこのchannel内部のchannelStatusが変化する。
現在のチャンネルの状態はこのchannelStatusを確認することで判断することが出来る。
この値はextensions.confであれば**${DIALSTAUTS}**で取得可能。
なお、帰ってくる値は対向のSIP Gatewayによる場合もあるので必ずしもこうなるとは限りません。
例えば楽天経由で050から0120番号へかけるとCONGESTIONが帰ってきます(050からは普通繋がらない)。
動作 | ステータス |
---|---|
宛先端末への疎通不可 | CHANUNAVAIL |
タイムアウト | CANCEL |
応答拒否 | BUSY |
応答なし | NOANSWER |
応答 | ANSWER |
混雑中 | CONGESTION |
https://wiki.asterisk.org/wiki/display/AST/Asterisk+13+Application_Dial
https://www.voip-info.org/asterisk-variable-dialstatus/
リファレンス等を見ると他にも色々ありますが、よく出てくるのは上記6つ程度かと思います。