Amazon Connect 問い合わせフローで休業日判定(Lambda + SSM Change Calendar)

Lambda + SSM Change Calendarで休業日判定してみた
2022.04.30

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Amazon Connect(以下、Connect)では、オペレーション時間を設定し、問い合わせ時刻により処理を分岐させることが可能です。一方、祝祭日や、会社の休業日などの設定は、Connect標準の機能だけでは行なえないので、少々作り込みが必要です。本ブログでは、Lambda + SSM Change Calendarを利用し休業日判定を実装してみたいと思います。以下のようなイメージです。 00

やってみた

SSM Change Calendar構築

祝祭日や、会社の休業日などを設定しておくChange Calendarを作成します。Change Calendarについては数クリックで作成できるので手順については割愛します。以下、ブログやドキュメントを参考にしてください。

ここではスケジュールイベントは設定せず作成しました。スケジュールイベントは動作確認にて追加します。

01

作成したChange Calendarの詳細を表示し「説明」の「Calendar use」よりARNを控えます。こちらのARNは、この後作成するLambdaで利用します。

02

Lambda構築

以下Lambdaの設定です。ランタイムはPython 3.9を利用しました。

環境変数

環境変数には、Change CalendarのARNを設定しました。

03

IAMロール

Lambdaにアタッチするロールは以下の権限を付与し、CloudWatch Logsへの書き込み権限や、Change Calendarの読み取り権限を付与しています。

  • AWSLambdaBasicExecutionRole
  • AmazonSSMReadOnlyAccess

関数コード

import os
import boto3
import json

ssm = boto3.client('ssm')

def lambda_handler(event, context):

    client = boto3.client('ssm')
    SSM_CALENDAR_ARN = os.environ['CALENDAR_ARN']
    
    try:
        # 環境変数に指定したカレンダーの状態取得
        response = client.get_calendar_state(
            CalendarNames=[
                SSM_CALENDAR_ARN,
            ]
        )
    except:
        print(f'カレンダー({SSM_CALENDAR_ARN})が見つかりませんでした。')
        return

    # カレンダーの状態ロギング
    print(json.dumps(response))
    
    state = response["State"]
    
    return {
        'CalState': state
    }

get_calendar_stateを使用して、Change Calendarのステータスを取得します。メソッドのパラメータには、ステータス取得の対象となる時刻指定が可能です。ここでは指定していませんので、Lambdaの実行時刻が使用されます。カレンダーが開いている場合は、 OPENステータス(実行可)、カレンダーが閉じられている場合、CLOSEDステータス(実行不可)が返されます。このステータスを祝祭日や、会社の休業日とし、後ほど作成する問い合わせフロー側で判定し処理を分岐させます。

Amazon Connect設定

Connectインスタンスの構築や、Lambda 関数の追加電話番号の設定等の手順は割愛します。今回の目的となる、休業日の判定のフローにのみフォーカスします。

問い合わせフロー

以下のフローを作成しました。

04

赤枠が、休業日の判定箇所となります。問い合わせブロックAWS Lambda 関数を呼び出すで、先程作成したLambdaを呼び出しています。

05

Lambdaの戻り値を、問い合わせ属性を確認するブロックで判定しています。

06

Lambdaの戻り値がOPENの場合は、エージェントに電話がつながり、 OPEN以外の場合は、プロンプトの再生ブロックで休業日であることをアナウンスします。

参考までに、エクスポートした問い合わせフローを置いておきます。(アカウントIDはマスクしています)

{"modules":[{"id":"3688becb-73c3-4f2e-8794-9b8e9e4bd20d","type":"SetLoggingBehavior","branches":[{"condition":"Success","transition":"813fa485-0718-43d9-9b37-cb5db8953503"}],"parameters":[{"name":"LoggingBehavior","value":"Enable"}],"metadata":{"position":{"x":640,"y":20}}},{"id":"0fd59fed-05dd-4d21-91b2-0364d92a25ce","type":"SetVoice","branches":[{"condition":"Success","transition":"368fc758-256f-40f7-9ac5-a0ef24041dda"}],"parameters":[{"name":"GlobalVoice","value":"Mizuki"}],"metadata":{"position":{"x":163,"y":21},"overrideConsoleVoice":false,"defaultVoice":"Standard"}},{"id":"b0f7e977-61e1-4485-a73f-3ca88b8f4ad0","type":"CheckAttribute","branches":[{"condition":"Evaluate","conditionType":"Equals","conditionValue":"OPEN","transition":"ce289eae-5f92-4c44-a42c-f12d32016082"},{"condition":"NoMatch","transition":"ac0a78f5-b409-4c5d-a6a7-732d3036be68"}],"parameters":[{"name":"Attribute","value":"CalState"},{"name":"Namespace","value":"External"}],"metadata":{"position":{"x":1121,"y":22},"conditionMetadata":[{"operator":{"name":"等しい","value":"Equals","shortDisplay":"="},"value":"OPEN","id":"e62382d0-0e43-4f6b-9fe5-239e9e3d9cd3"}]}},{"id":"9a980744-c801-45c6-b6a9-0b5ec6e40dce","type":"Transfer","branches":[{"condition":"AtCapacity","transition":"ce2fbc5c-72c5-45fb-a502-4e39d3f6cdd3"},{"condition":"Error","transition":"ce2fbc5c-72c5-45fb-a502-4e39d3f6cdd3"}],"parameters":[],"metadata":{"position":{"x":1601,"y":19},"useDynamic":false,"queue":null},"target":"Queue"},{"id":"ce2fbc5c-72c5-45fb-a502-4e39d3f6cdd3","type":"Disconnect","branches":[],"parameters":[],"metadata":{"position":{"x":1821,"y":20}}},{"id":"ac0a78f5-b409-4c5d-a6a7-732d3036be68","type":"PlayPrompt","branches":[{"condition":"Success","transition":"1b1edfc9-3e71-4779-9993-8fab85424bfc"},{"condition":"Error","transition":"1b1edfc9-3e71-4779-9993-8fab85424bfc"}],"parameters":[{"name":"Text","value":"今日は休みです","namespace":null},{"name":"TextToSpeechType","value":"text"}],"metadata":{"position":{"x":1360,"y":221},"useDynamic":false}},{"id":"ce289eae-5f92-4c44-a42c-f12d32016082","type":"SetEventHook","branches":[{"condition":"Success","transition":"9a980744-c801-45c6-b6a9-0b5ec6e40dce"},{"condition":"Error","transition":"1b1edfc9-3e71-4779-9993-8fab85424bfc"}],"parameters":[{"name":"Type","value":"CustomerQueue"},{"name":"ContactFlowId","value":"arn:aws:connect:ap-northeast-1:XXXXXXXXXXXX:instance/f248cbab-9769-4766-bd6d-bd42de1168ea/contact-flow/4382068c-cc34-45ea-b3c2-d2e83a2af6d8","resourceName":"test-customer-queue"}],"metadata":{"position":{"x":1362,"y":21},"contactFlow":{"id":"arn:aws:connect:ap-northeast-1:XXXXXXXXXXXX:instance/f248cbab-9769-4766-bd6d-bd42de1168ea/contact-flow/4382068c-cc34-45ea-b3c2-d2e83a2af6d8","text":"test-customer-queue"},"customerOrAgent":true,"useDynamic":false}},{"id":"1b1edfc9-3e71-4779-9993-8fab85424bfc","type":"Disconnect","branches":[],"parameters":[],"metadata":{"position":{"x":1604,"y":223}}},{"id":"813fa485-0718-43d9-9b37-cb5db8953503","type":"InvokeExternalResource","branches":[{"condition":"Success","transition":"b0f7e977-61e1-4485-a73f-3ca88b8f4ad0"},{"condition":"Error","transition":"3ef70bda-a376-4bdc-b90f-b13dbec3153e"}],"parameters":[{"name":"FunctionArn","value":"arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:test-lambda","namespace":null},{"name":"TimeLimit","value":"3"}],"metadata":{"position":{"x":878,"y":21},"dynamicMetadata":{},"useDynamic":false},"target":"Lambda"},{"id":"368fc758-256f-40f7-9ac5-a0ef24041dda","type":"SetQueue","branches":[{"condition":"Success","transition":"3688becb-73c3-4f2e-8794-9b8e9e4bd20d"},{"condition":"Error","transition":"3ef70bda-a376-4bdc-b90f-b13dbec3153e"}],"parameters":[{"name":"Queue","value":"arn:aws:connect:ap-northeast-1:XXXXXXXXXXXX:instance/f248cbab-9769-4766-bd6d-bd42de1168ea/queue/46d3c9ab-3b9a-47c4-be49-17cc2385f674","namespace":null,"resourceName":"test-queue"}],"metadata":{"position":{"x":401,"y":20},"useDynamic":false,"queue":{"id":"arn:aws:connect:ap-northeast-1:XXXXXXXXXXXX:instance/f248cbab-9769-4766-bd6d-bd42de1168ea/queue/46d3c9ab-3b9a-47c4-be49-17cc2385f674","text":"test-queue","arn":null,"metricDetail":null,"resourceId":null}}},{"id":"3ef70bda-a376-4bdc-b90f-b13dbec3153e","type":"Disconnect","branches":[],"parameters":[],"metadata":{"position":{"x":1118,"y":221}}}],"version":"1","start":"0fd59fed-05dd-4d21-91b2-0364d92a25ce","metadata":{"entryPointPosition":{"x":15,"y":21},"snapToGrid":false,"name":"test-contact-flow","description":null,"type":"contactFlow","status":"published","hash":"26045b98e7bcc131433e95ed242b39d21d14c52cc310ecf718bfe9690ddf2d8c"},"type":"contactFlow"}

動作確認

それでは、休業日判定(Change Calendarのイベント設定有無)を試してみたいと思います。現在、Change Calendarにイベントは設定していませんので、エージェントに電話がつながることが期待する結果となります。

手元の電話から架電し、想定通りエージェントに電話がつながりました。

07

問い合わせフローでロギングを有効化していたので、ログから処理内容を確認します。

08

Lambdaが呼び出しされ、戻り値にOPENが返されていることがわかります。問い合わせ属性を確認(CheckAttribute)後、顧客キューフローの設定(SetContactFlow/CustomerQueue)に遷移しています。

次に、休業日を設定(Change Calendarにイベントを追加)しました。

09

今回は、エージェントに電話がつながらないことが期待する結果となります。手元の電話から架電しましたが、想定通りエージェントに電話はつながりませんでした。ログを確認すると、Lambdaの戻り値はCLOSEで、休業日アナウンスのプロンプトが再生されていることがわかります。

10

最後に

今回は、Lambda + SSM Change Calendarを利用し、Amazon Connectで休業日を判定してみました。休業日判定の手段の一つとして参考になれば幸いです。休業日判定のニーズは多いと思いますので、今後のアップデートにも期待したいと思います!

参考