Amazon Connect ~WEBフォーム連携~

こんにちは。katoです。

今回はお問い合わせページ等のWEBフォームで入力された情報をAmazon Connectに連携する方法をご紹介いたします。

 

概要

通常、Amazon Connectのコールフローでは、ダイアル番号や顧客入力(DTMF)等の内容しか問い合わせ情報として利用することができず、お問い合わせページのようなリンクサイトを用意しても、そのフォームの入力情報での処理分岐等を行うことができません。

ただし、DTMFの先行入力を利用し、Lambda連携することでお問い合わせ情報に基づいたコールフローの分岐を実現することが可能です。

今回ご紹介する構成は下図の様になります。

 

Amazon Connect WEBフォーム連携 構成図

 

S3の静的ウェブサイトホスティングの機能を利用し、お問い合わせページを用意したサーバーレスの構成となります。

お問い合わせページの入力内容をAPI Gateway経由でLambdaにPOSTし、返り値として電話発信用のHTMLコードを返すといった内容となります。

フォーム入力等のお問い合わせ情報はDynamoDBに格納することで、Amazon Connectのコールフローでもお問い合わせページ等での入力に基づいた処理分岐が可能となります。

なお、今回ご紹介する仕組みはスマートフォン等からのお問い合わせを想定しております。

HTMLのhrefで電話番号を指定する形となりますので、PC等からのお問い合わせには対応するアプリ等が必要となる可能性が御座います。

 

手順

それでは実際に構築していきます。

DynamoDB

まず、問い合わせ情報を保存するDynamoDBテーブルを作成します。

今回はプライマリパーティションキーを「case(文字列)」としてテーブルを作成しています。

ソートキーは設定しておりません。

Lambda(API Gateway連携用)

次にAPI Gatewayに連携するLambda関数を作成します。

なお、今回の利用するLambda関数はすべてPython 3.7での作成となります。

#-*- coding: utf-8 -*-
import json
import boto3
import random
 
dynamodb = boto3.client('dynamodb')
 
def lambda_handler(event, context):
    jsondata = {}
    data = event['body']
    datas = data.split("&")
    for num in range(len(datas)):
        param = datas[num].split('=')
        jsondata[param[0]] = param[1]
    print(jsondata)
    if (jsondata['caseid'] == ""):
        result = ""
        while (result != "OK"):
            caseid = random.randrange(0, 9999, 4)
            getitem = dynamodb.get_item(
                TableName='<DynamoDB TableName>',
                Key={
                    'case': {
                        'S': str(caseid)
                    }
                }
            )
            if ("Item" in getitem.keys()):
                result = "NG"
            else:
                result = "OK"
        if (jsondata['corp'] == ""):
            corp = "guest"
        else:
            corp = jsondata['corp']
        if (jsondata['name'] == ""):
            name = "guest"
        else:
            name = jsondata['name']
        putitem = dynamodb.put_item(
            TableName='<DynamoDB TableName>',
            Item={
                'case': {
                    'S': str(caseid)
                },
                'corp': {
                    'S': str(corp)
                },
                'name': {
                    'S': str(name)
                }
            }
        )
        body = "<body Onload=\"location.href=\'tel:+81<電話番号>,"+ str(caseid) +"\'\"></body>"
    else:
        body = "<body Onload=\"location.href=\'tel:+81<電話番号>,"+ str(jsondata['caseid']) +"\'\"></body>"
    return {"body": body}

 

基本的にはHTML + API Gateway + Lambdaで一般的に利用するようなコードなので細かい内容は省略します。

 

簡単に処理の内容を説明すると次の通りです。

ケースIDあり → 入力されたケースIDをDTMFとしてAmazon Connectの電話番号に付与

ケースIDなし → ユニークなケースIDを発行

         新規問い合わせとしてDynamoDBにアイテム登録

         発行したケースIDをDTMFとしてAmazon Connectの電話番号に付与

 

hrefで指定した電話番号の末尾に「,0123」といったような形で番号を追加することでAmazon Connectへの転送後に、自動的にDTMF入力が行われます(事前にDTMFが入力された状態)。

この仕組みを利用することで、お客様に何度もケースIDを入力していただくのを避けるだけでなく、Amazon Connect側でのエントリ間のディレイの調整などの面倒な設計も不要になります。

 

API Gateway

 

次はAPI Gatewayの設定になります。

Lambda連携のPOSTメソッドを作成するだけなので、設定が必要な部分のみピックアップしてご説明します。

 

①統合リクエス

下記のマッピングテンプレートを設定します。

Content-Type: application/x-www-form-urlencoded

マッピングテンプレート本文: {"body": $util.urlDecode($input.json("$"))}

 

②統合レスポンス

Lambdaエラーの正規表現: .*

メソッドレスポンスのステータス: 302

コンテンツの処理: パススルー

レスポンスヘッダー: Location

マッピングの値: integration.response.body.location

Content-Type: text/html

マッピングテンプレート本文: $input.path('$.body')

 

③メソッドレスポンス

HTTPのステータス: 302

名前: Location

コンテンツタイプ: text/html

モデル: Empty

 

リクエストは文字化け対応、レスポンスはhtmlの設定になります。

APIをデプロイして呼び出しURLを控えておきます。

 

S3(静的ウェブサイトホスティング

 

次にS3の静的ウェブサイトホスティングの設定を行います。

 

静的ウェブサイトホスティング有効化して、html配置するのみなので設定方法は省略し、htmlコードのみ記載させていただきます。

 

<!DOCTYPE html>
<html lang="ja">
<title>Amazon Connect DTMF</title> 
<head>
<meta charset="UTF-8">
<style type="text/css">
.select1 .hidden1 {
  pointer-events: none;
  background-color: #DCDCDC;
  text-align: center;
  width: 900px;
  margin: auto;
}
.select1 .hidden1 .corp {
  background-color: #A9A9A9;
  margin: 0px 10px 0px 30px;
  font-family: 'メイリオ',Meiyro;
}
.select1 .hidden1 .name {
  background-color: #A9A9A9;
  margin: 0px 10px 0px 58px;
  font-family: 'メイリオ',Meiyro;
}
.select1 input:checked ~ .hidden1 {
  pointer-events: auto;
  background-color: #FFDEAD;
}
.select1 input:checked ~ .hidden1 .corp {
  background-color: #ffffff;
}
.select1 input:checked ~ .hidden1 .name {
  background-color: #ffffff;
}
.select2 .hidden2 {
  pointer-events: none;
  background-color: #DCDCDC;
  text-align: center;
  width: 900px;
  margin: auto;
}
.select2 .hidden2 .case {
  background-color: #A9A9A9;
  margin: 0px 10px 0px 20px;
  font-family: 'メイリオ',Meiyro;
}
.select2 input:checked ~ .hidden2 {
  pointer-events: auto;
  background-color: #FFDEAD;
}
.select2 input:checked ~ .hidden2 .case {
  background-color: #ffffff;
}
</style>
</meta>
</head>
<body>
<form action="<API Gateway呼び出しURL>" method="post">
  <div style="text-align: center; width: 900px; margin: auto;">
    <div style="text-align: left; margin: 10px 0px 10px 0px; font-family: 'メイリオ',Meiyro">
      <div class="select1">
        <input type="radio" name="type" id="new" value="new" checked="checked">新規のお問い合わせ
        <div class="hidden1" style="text-align: center; width: 900px; margin: auto;">
          <div style="margin: 0px 0px 0px 150px; display: flex; justify-content: center; align-items: center">
            <div>
              会社名/団体名
            </div>
            <div style="margin: 10px 10px 10px 0px; font-family: 'メイリオ',Meiyro; text-align: left; width: 66%;">
              <input class="corp" type="text" name="corp">
            </div>
          </div>
          <div style="margin: 0px 0px 0px 150px; display: flex; justify-content: center; align-items: center">
            <div>
              お名前
            </div>
            <div style="margin: 10px 10px 10px 0px; font-family: 'メイリオ',Meiyro; text-align: left; width: 66%;">
              <input class="name" type="text" name="name">
            </div>
          </div>
        </div>
      </div>
      <div class="select2">
        <input type="radio" name="type" id="case" value="case">ケースの再オープン
        <div class="hidden2" style="text-align: center; width: 900px; margin: auto;">
          <div style="margin: 0px 0px 0px 150px; display: flex; justify-content: center; align-items: center">
            <div>
              お問い合わせ番号
            </div>
            <div style="margin: 10px 10px 10px 0px; font-family: 'メイリオ',Meiyro; text-align: left; width: 66%;">
              <input class="case" type="text" name="caseid">
            </div>
          </div>
        </div>
      </div>
    </div>
    <input type='submit' value='問い合わせ' style="background-color: #FFA500; border-radius: 10px; font-size: 20px; padding: 10px; color:#ffffff; font-family: 'メイリオ',Meiyro">
  </div>
  <style type="text/css">
    p {font-weight: bolder }
  </style>
</form> 
</body>
</html>

 

多少それっぽいデザインにはしていますが、今回の処理自体には影響しないので、詳細は省略させていただきます。

POST先として先程メモしたAPI Gatewayの呼び出しURLを指定するだけです。

 

問い合わせまでの部分が以上の設定で完了となるので、ここからAmazon Connect部分の設定となります。

 

今回の問い合わせフローの概要は下図の通りとなります。

Amazon Connect  問い合わせフロー

 

フローを作成する前に、フローで利用するLambda関数を作成します。

フロー種別ごとに以下のLambda関数をそれぞれ利用します。

 

・アイテム検索用関数(問い合わせフロー)

・アイテム作成&更新&メッセージ作成用関数(顧客ウィスパーフロー)

・アイテム検索&メッセージ作成用関数(エージェントウィスパーフロー)

 

 

アイテム検索用関数
#-*- coding: utf-8 -*-
import json
import boto3
 
dynamodb = boto3.client('dynamodb')
 
def lambda_handler(event, context):
    caseid = event['Details']['Parameters']['caseid']
    getobj = dynamodb.get_item(
        TableName='<DynamoDB TableName>',
        Key={
            'case': {
                'S': caseid
            }
        }
    )
    if ("agent" in getobj['Item'].keys()):
        agent = getobj['Item']['agent']['S']
    else:
        agent = "empty"
    return {'agent': agent}

 

DTMFとして入力されたケースIDを基にDynamoDBから問い合わせ情報を取得しております。

 

agent項目あり(ケースの再オープン)の場合、担当オペレータの情報をAmazon Connectに返します。

agent項目なし(新規問い合わせ)の場合はagentを「empty」としてAmazon Connectに返します。

 

 

アイテム作成&更新&メッセージ作成用関数
#-*- coding: utf-8 -*-
import json
import boto3
import random
 
dynamodb = boto3.client('dynamodb')
 
def lambda_handler(event, context):
    params = agent = event['Details']['Parameters']
    if ("status" in params.keys()):
        result = ""
        while (result != "OK"):
            caseid = random.randrange(0, 9999, 4)
            getitem = dynamodb.get_item(
                TableName='<DynamoDB TableName>',
                Key={
                    'case': {
                        'S': str(caseid)
                    }
                }
            )
            if ("Item" in getitem.keys()):
                result = "NG"
            else:
                result = "OK"
        corp = "guest"
        name = "guest"
        putitem = dynamodb.put_item(
            TableName='<DynamoDB TableName>',
            Item={
                'case': {
                    'S': str(caseid)
                },
                'agent': {
                    'S': agent
                },
                'corp': {
                    'S': str(corp)                
                     
                },
                'name': {
                    'S': str(name)
                }
            }
        )
    else:
        agent = event['Details']['Parameters']['agent']
        caseid = event['Details']['Parameters']['caseid']
        getitem = dynamodb.get_item(
            TableName='<DynamoDB TableName>',
            Key={
                'case': {
                    'S': str(caseid)
                }
            }
        )
        corp = getitem['Item']['corp']['S']
        name = getitem['Item']['name']['S']
        putitem = dynamodb.put_item(
            TableName='<DynamoDB TableName>',
            Item={
                'case': {
                    'S': str(caseid)
                },
                'agent': {
                    'S': agent
                },
                'corp': {
                    'S': corp
                },
                'name': {
                    'S': name
                }
            }
        )
    message = "お客様のお問い合わせ番号は" + str(caseid) + "となります。"
    return {'message': message}

 

「ststus」キーあり(ケースIDなしの問い合わせ)の場合、新規ケースIDの発行とともに、ゲストユーザとしてDynamoDBにアイテムを新規登録します。

※お問い合わせページを経由することなく、電話番号を直接指定してお問い合わせがなされた場合の処理となります。

※ケースIDはユニークな値が生成されるまでループの処理が行われます。ご利用方法によっては、Amazon Connect側でのLambda連携のタイムアウト値(8秒)を超える可能性が御座います。

 

「status」キーなし(ケースIDありの問い合わせ)の場合、担当オペレータをDynamoDBに追加します。

 

エージェントウィスパーフローでのお客様への再生メッセージとして、発行されたケースIDを含むメッセージをAmazon Connectに返します。

 

 

アイテム検索&メッセージ作成用関数
#-*- coding: utf-8 -*-
import json
import boto3
 
dynamodb = boto3.client('dynamodb')
 
def lambda_handler(event, context):
    caseid = event['Details']['Parameters']['caseid']
    getobj = dynamodb.get_item(
        TableName='<DynamoDB TableName>',
        Key={
            'case': {
                'S': caseid
            }
        }
    )
    corp = getobj['Item']['corp']['S']
    name = getobj['Item']['name']['S']
    if corp == "guest" and name == "guest":
        message = "ゲストユーザ様からのお問い合わせです。"
    elif corp == "guest":
        message = str(name) + "様からお問い合わせです。"
    elif name == "guest":
       message = str(corp) + "のお客様からお問い合わせです。"
    else:
        message = str(corp) + "の" + str(name) + "様からのお問い合わせです。"
    return {'message': message}

 

ケースIDを基にDynamoDBからアイテム検索を行い、エージェントへの通知用のメッセージをAmazon Connectに返します。

以上でAamzon Connectで利用するLambda関数の設定は完了となります。

後はAmazon Connectのコールフローを作成すれば完了です。

コールフローの設定に関しましては、パラメータの指定や注意点をピックアップしてご説明していきます。

フローの流れはフロー概要やキャプチャをご参照ください。

 

問い合わせフロー

Amazon Connect  問い合わせフロー

 

・顧客の入力を保存する
このフローにて電話番号に付与されたDTMF入力が行われます。
今回は短めのプロンプトメッセージを指定し、エントリ間のディレイを2秒に設定しております。

DTMFの入力はプロンプトの終了を待たずに実施されるので、長いメッセージを指定しても途中で入力が行われ中断されてしまいますので注意が必要です。

また、エントリ間のディレイは必ず2秒以上にしてください。

1秒を指定した場合には、DTMFの先行入力を行っていてもエラーの処理となってしまいます。

 

・問い合わせ属性を確認する①
DTMFの有無を判定しております。

今回のケースでは、「>= 0(0以上)」のチェック条件を追加することで、ケースIDを含んでいるかを判定しております。

AWS Lambda関数を呼び出す
ケースIDが含まれている場合には、アイテム検索用のLambda関数を呼び出します。

今回は保存済みのお客様入力を「caseid」の宛先キーでパラメータ指定しております。

・問い合わせ属性を確認する②
Lambda関数の返り値(agent)が「empty」であるかを判定します。

 

・問い合わせ属性の設定①
Lambda関数の返り値が「empty」の場合、ケースIDと「agent」を問合せ属性として設定します。
このフローを追加することで、ウィスパーフローに対して問い合わせ属性を連携することが可能になります。

・問い合わせ属性の設定②
Lambda関数の返り値が「empty」でない場合には、動的にエージェントをキュー設定して、ケースIDを問合せ属性として設定します。

 

エージェントウィスパーフロー

 

Amazon Connect エージェントウィスパーフロー

 

アイテム検索&メッセージ作成用のLambda関数を呼び出します。

パラメータとしてユーザー定義の「caseid」を指定します。

 

Lambda関数の返り値をそのままプロンプトにて再生しております。

 

 

顧客ウィスパーフロー

 

Amazon Connect 顧客ウィスパーフロー

 

agentの値が「empty」かどうかで処理を分岐させております。

呼び出すLambda関数は同じものとなっておりますが、パラメータの指定が異なってきます。

 

agent = empty → 「caseid」と「agent」をパラメータ指定

一致無し → 「guest」の値を「status」の宛先キーとして指定(テキストの使用)

       「agent」の宛先キーで「エージェント/ユーザー名」の属性を指定

 

Lambdaの返り値をそのままプロンプトにて再生しております。

 

動作確認

静的ウェブサイトホスティングのHTMLサイトにアクセスします。

 

ケースタイプの選択が可能なお問い合わせページとなっております。

Amazon Connect  お問い合わせページ

 

新規のお問い合わせの場合、ユニークなケースIDが発行され、ケースの再オープンの場合、指定されたケースIDにて問い合わせが行われます。

どちらの場合も、問い合わせが行われると、API Gatewayを経由してLambdaからHTMLのレスポンスが返ってきます。

このレスポンスは、上記のケースIDをAmazon Connect電話番号にDTMFとして付与したものとなります。

 

 

上記はiPhoneから問い合わせを実施した例となりますが、レスポンスを受けて電話発信を行うことが可能となります。

なお、新規問い合わせの場合、このタイミングにてDynamoDBにアイテムが登録されます。

てDynamoDBにアイテム登録

agent項目はこの後の顧客ウィスパーフローにて担当エージェントの情報が挿入されます。

この後のフローの動作と致しましては、前述のフロー概要の通りとなっております。

新規の問い合わせの場合には、顧客ウィスパーフローにてケースIDが通知され通話が確立、ケースの再オープンの場合には、エージェントウィスパーフローにて問い合わせ情報(企業名等)が通知されます。

 

まとめ

今回はAmazon Connectに対してWEBフォームの入力情報を連携する方法をご紹介させていただきました。

DTMFの先行入力を利用した少し特殊な連携方法となりますが、コールフローにてフォーム情報に基づいた処理分岐が可能になるので、利用シーンの幅が広がるのではないかと思います。

今回のシステムはAWSサービスのみで構築されたサーバーレスの構成となるので、管理の手間も少なく、簡単に導入することが可能となっております。

CRM製品との連携の際にも利用可能かと思いますので、ご興味のある方はお試しになってみてはいかがでしょうか。

 

 

 

このブログの著者

 

  AWS相談会バナー  

おてがるCTIバナー