アスタリスクを使用したIVRのテスト...アスタリスク

最近、SLAを計算するために、リモートメンテナンスサービスのアップタイムメトリックを追加する必要がありました。 API呼び出しの統計は、操作性の間接的な指標であり、外部ネットワークからのダイヤルからサービスメニュー全体へのユーザーの通過まで、すべての機能の信頼性の高いチェックが必要です。 インターネットで何も準備ができていなかったので、研究を共有することにしました。







リモートサービスシステムがあります-クライアントはコールセンターを呼び出して、オペレーターの介入なしに自分のアカウントの設定を確認/変更できます。 トーン(DTMF)は、メニューの操作と設定の制御に使用されます。 次に、PBXはAPIを介してメインシステムのコアと対話し、音声メッセージの形式で結果をユーザーに返します。







タスク :自動システムチェックをセットアップします(要求に正しく応答し、必要なコマンドを実行します)。

主な要件:









この記事では、コールセンターとAPIを簡素化して不名誉を与えます。コールセンターを呼び出すと、ユーザーは唯一のサービス(キー1)を使用できます。 その中で、ユーザーはPINを入力するように求められ、それが正しい場合、ユーザーアカウントのステータス(ON / OFF)が発行されます。 左または右にステップ-エラーメッセージが表示されます。 APIでは、GET ping(呼び出しの初期化)、GETステータス(ステータスの取得)、POSTステータス(ステータスの設定)の3つのメソッドを使用できます。







IVRフローチャート







アスタリスクを使用して問題を解決します。 実際、クライアントに代わってのみ同様のIVRを収集する必要があります:ステートマシンについて説明する必要があり(挨拶を待っている、PIN要求を待っているなど)、各状態に移行するときに特定のアクションを実行する必要があります。







コマンドの送信方法は明らかです。コールセンターはユーザーからのトーンを期待します。つまり、 SendDTMF



コマンドを使用して、クライアントに代わって必要なボタンを「押す」ことができます。







そして、あなたの状態を変える方法は? はい、まったく同じです! これを行うために、重要な場所で簡単なマクロを呼び出すことにより、戦闘IVRのダイヤルプランをわずかにアップグレードします。







 [macro-robot] exten => s,1,ExecIf($["${CALLERID(name)}"!="Robot"]?MacroExit()) same => n,Wait(1) same => n,SendDTMF(${ARG1})
      
      





その結果、IVRの主要な場所で、ロボットからの呼び出しの場合、選択されたDTMFシーケンスがチャネルに送信されます。 ロボットが入力スタンバイモードに移行できるように、1秒の遅延が追加されました。







これで、アスタリスクの機能を使用して、必要なローカルコンテキストに呼び出しを送信できます。したがって、コールセンターとロボットのIVRを閉じます。 最も単純なケースでは、呼び出しファイルを使用して、このファイルを定期的に/var/spool/asterisk/outgoing/



コピーすることでテストを実行できます。







検証プロセスは次のとおりです。







1.コールセンターに電話する







2.サービスを選択できるようになるまでお待ちください







3. 「1」をクリックします







4. PINを入力できるようになるまでお待ちください







5. PINを入力します







6.状態を調べる







-API呼び出しによる最初のチェックで、状態を反対に変更し、状態を再確認します(ステップ3に進みます)







-2番目のチェックでは、状態が反対に変更されていることを確認します







8.条件が変更された場合、検証が成功したと見なします







9.その他の場合はすべて、エラーを報告します







以下に、両方のアスタリスクのダイヤルプランを「1つの画面で」組み合わせ、制御/状態の変更がどのように転送されるかを示します。







相互作用スキーム







画像ではなくテキスト

スポイラーに隠された、tk。 画面を引き裂きます。







  # Call-file Channel: SIP/cc_peer/ivr >----------\ Callerid: Robot | /--< Context: robot-test | | Extension: s | | | | | [robot-test] | | [ivr] | | exten => s,1,NoOp(Wait for init) <--/ \-----> exten => s,1,NoOp(IVR Start) same => n,Set(STATUS=) same => n,Answer() same => n,WaitExten(10) same => n,GotoIf($["${CURL(localhost/ping)}"!="PONG"]?err,1) /----------------------< same => n,Macro(robot,00) exten => 00,1,NoOp(Init done) <------------------------------/ same => n,Background(welcome) same => n,Wait(1) same => n(again),Background(press_1_for_status) same => n,SendDTMF(1) ; Press 1 for status >----------------\ same => n,WaitExten(5) same => n,WaitExten(10) \ same => n,Goto(err,1) \ exten => 10,1,NoOp(Status check) <---------------------------\ \--------------------> exten => 1,1,NoOp(Status check) <---------------------------------\ same => n,Wait(1) \-------------------------< same => n,Macro(robot,10) | same => n,SendDTMF(1234) ; Send pin code >----------------------------------------> same => n,Read(PIN,enter_pin,4) | same => n,WaitExten(10) same => n,Set(STATUS=${CURL(localhost/status?pin=${PIN})}) | same => n,GotoIf($["${STATUS}"=="ON"]?on) | exten => _1[12],1,NoOp(Status) <--------------------------------------------------\ same => n,GotoIf($["${STATUS}"=="OFF"]?off) | same => n,ExecIf($[${EXTEN}==11]?MSet(CURRENT=ON,NEW=OFF)) | same => n,Goto(err,1) | same => n,ExecIf($[${EXTEN}==12]?MSet(CURRENT=OFF,NEW=ON)) |---< same => n(on),Macro(robot,11) | same => n,GotoIf($["${STATUS}"==""]?toggle) | same => n,Background(status_on) | same => n,ExecIf($["${STATUS}"=="${CURRENT}"]?System(echo GOOD >> /cc_check.log)) | same => n,Goto(s,again) | same => n,ExecIf($["${STATUS}"!="${CURRENT}"]?System(echo BAD >> /cc_check.log)) \---< same => n(off),Macro(robot,12) | same => n,Hangup() same => n,Background(status_off) | same => n(toggle),NoOp(Toggle status) same => n,Goto(s,again) | same => n,Set(STATUS=${NEW}) /--------------------------------------------------------------------------------------------/ same => n,Set(RES=${CURL(localhost/status,status=${NEW})}) / exten => err,1,NoOp(Error occured) same => n,Wait(1) / /------< same => n,Macro(robot,99) same => n,SendDTMF(1) ; Press 1 for status >--------------------/ / same => n,Playback(error) same => n,WaitExten(10) / same => n,Hangup() / exten => i,1,System(echo BAD >> /cc_check.log) <---------------------------/ [macro-robot] exten => s,1,ExecIf($["${CALLERID(name)}"!="Robot"]?MacroExit()) same => n,Wait(1) same => n,SendDTMF(${ARG1})
      
      





この例では、チェックの結果は/cc_check.log



ファイルに書き込まれます。 もちろん、戦闘システムでは、これらの結果を監視システムに追加します。







実際のシステムでは、リモートサービスシステム全体をチェックするためのAPIへの単純なCURLリクエストでは不十分である可能性が高いため、ソリューションを拡張して、AMIを介してロボットの呼び出しを制御できます。 これを行うには、ロボットがUserEvent'



をAMIに送信するようにロボットのUserEvent'



を変更する必要があります。 デモ構成は次のように変更できます。







robot-ami.conf
 [robot-test-ami] exten => s,1,UserEvent(CC_ROBOT_WAIT_INIT,RobotId: ${RobotId}) same => n,Set(STATUS=) same => n,WaitExten(10) exten => 00,1,UserEvent(CC_ROBOT_WAIT_SERVICE,RobotId: ${RobotId}) same => n,Wait(1) same => n,UserEvent(CC_ROBOT_WAIT_SERVICE,RobotId: ${RobotId},Data: Will press 1 now) same => n,SendDTMF(1) ; Press 1 for status same => n,WaitExten(10) exten => 10,1,UserEvent(CC_ROBOT_WAIT_PIN,RobotId: ${RobotId}) same => n,Wait(1) same => n,UserEvent(CC_ROBOT_WAIT_PIN,RobotId: ${RobotId},Data: Will send pin (1234) now) same => n,SendDTMF(1234) ; Send pin code same => n,WaitExten(10) exten => _1[12],1,UserEvent(CC_ROBOT_STATUS_CHECK,RobotId: ${RobotId}) same => n,ExecIf($[${EXTEN}==11]?MSet(CURRENT=ON,NEW=OFF)) same => n,ExecIf($[${EXTEN}==12]?MSet(CURRENT=OFF,NEW=ON)) same => n,UserEvent(CC_ROBOT_STATUS_CHECK,RobotId: ${RobotId},Data: Current status is '${CURRENT}') same => n,GotoIf($["${STATUS}"==""]?toggle) same => n,ExecIf($["${STATUS}"=="${CURRENT}"]?UserEvent(CC_ROBOT_RESULT,RobotId: ${RobotId},Data: GOOD)) same => n,ExecIf($["${STATUS}"!="${CURRENT}"]?UserEvent(CC_ROBOT_RESULT,RobotId: ${RobotId},Data: BAD)) same => n,Hangup() same => n(toggle),UserEvent(CC_ROBOT_STATUS_CHECK,RobotId: ${RobotId},Data: Need to toggle state) same => n,Set(STATUS=${NEW}) same => n,UserEvent(CC_ROBOT_TOGGLE,RobotId: ${RobotId},Data: ${CURRENT}) same => n,Wait(2) same => n,SendDTMF(1) same => n,WaitExten(10) exten => i,1,UserEvent(CC_ROBOT_RESULT,RobotId: ${RobotId},Data: BAD)
      
      





このようなUserEvent'



するには、AMIを介してロボットの呼び出しが開始されるアスタリスクに接続し、 Originate



を作成し、着信UserEvent'



に従って引き続き動作するUserEvent'



ます。 Pythonでのデモチェックのスクリプトの実装例:







test_call.py
 #!/usr/bin/python import os import time import string import random import sys import requests from asterisk.ami import Action, AMIClient seconds_to_wait = 30 test_result = 'unknown' host = 'localhost' # Asterisk with AMI and test dialplan user = 'robot' # AMI user password = 'MrRobot' # AMI password call_to = 'Local/ivr' # Call-center context = { # Robot dialplan context "context": "robot-test-ami", "extension": "s", "priority": 1 } def toggle_state(new_state): print 'Will try to toggle state to {}'.format(new_state) r = requests.post('http://localhost/status', data = {'status':new_state.upper()}) print 'Done! Actual state now: {}'.format(r.text) def event_notification(source, event): global test_result keys = event.keys if 'RobotId' in keys: if keys['RobotId'] == robot_id: # it's our RobotId if 'Data' in keys: data = keys['Data'] else: data = 'unknown' if 'UserEvent' in keys: name = keys['UserEvent'] else: name = 'unknown' if name.startswith('CC_ROBOT'): print '{}: {}'.format(name, data) if name == 'CC_ROBOT_TOGGLE': if data.lower() in ['on','off']: if data.lower() == 'on': toggle_state('off') else: toggle_state('on') else: print 'Unknown state {}'.format(data) if name == 'CC_ROBOT_RESULT': test_result = data # Generate uniq RobotId to distinguish events from different robots robot_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) print 'Current RobotId: {}'.format(robot_id) # Action to enable user events aEnableEvents = Action('Events', keys={'EventMask':'user'}) # Action to originate call aOriginateCall = Action('Originate', keys={'Channel':call_to, 'Context':context['context'], 'Exten':context['extension'], 'Priority':context['priority'], 'CallerId':'Robot'}, variables={'RobotId':robot_id} ) # Init AMI client and try to login client = AMIClient(host) # Register our event listener client.add_event_listener(event_notification) try: future = client.login(user, password) # This will wait for 1 second or fail if future.response.is_error(): raise Exception(str(future.response.keys['Message'])) except Exception as err: client.logoff() sys.exit('Error: {}'.format(err.message)) print 'Spawned AMI session to: {}'.format(host) try: # Try to enable user events coming future = client.send_action(aEnableEvents,None) if future.response.is_error(): raise Exception(str(future.response.keys['Message'])) print 'Logged in as {}'.format(user) # Try to originate call future = client.send_action(aOriginateCall,None) if future.response.is_error(): raise Exception(str(future.response.keys['Message'])) print 'Originated test call' except Exception as err: client.logoff() sys.exit('Error: {}'.format(err.message)) print 'Waiting for events...' # Wait for events during timelimit interval for i in range(seconds_to_wait): time.sleep(1) # If test_result is changed (via events), then stop waiting if test_result != 'unknown': break; else: client.logoff() sys.exit('Error: time limit exceeded') # Logoff if we still here client.logoff() print 'Test result: {}'.format(test_result)
      
      





呼び出しファイルを使用して上記のシナリオと同様にアクションが実行されますが、呼び出しはスクリプトコマンドによって開始され、API呼び出しはCC_ROBOT_TOGGLE



イベントのCC_ROBOT_TOGGLE



時にスクリプトからも行われます。 チェックの結果には、 CC_ROBOT_RESULT



イベントが伴います。







その結果、必要な条件のフレームワーク内で問題を解決しました。呼び出して「ボタンを押す」、戦闘ダイヤルプランでチェックを行う(ロボットが鳴った場合に呼び出し中にトーンを出す)。







いいテストをしてください!








All Articles