簡易ヘルプデスクボットの作成

# はじめに
Amazon Connect、Lambda、Lex、DynamoDBを使って、ヘルプデスクボットを作成してみました。

# 完成イメージ
![スクリーンショット 2023-11-13 120029.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/3745e6d5-db97-7284-1435-449cdabdbc6c.png)


# Work1: コンタクトフロー作成
## Step 1: LambdaとDynamoDBによるサーバーレスアプリの構築
### 1. DynamoDBの作成
①AWS コンソール から Amazon DynamoDB を選択し、`テーブルの作成`を選択します。
②テーブルの作成ページで、テーブル名を `connectDemo`、パーティションキーを `phoneNumber `文字列に設定します。
![スクリーンショット 2023-11-13 092644.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/b2b68b5f-8411-1d4d-41fe-736906ccd567.png)
③その他のオプションはデフォルトのまま、`テーブルの作成`を選択します。
④`connectDemo テーブルは正常に作成されました。`が表示されれば成功です。

### 2. 新しい IAM ロールの作成
①AWS コンソール から IAM を選択し、アクセス管理セクションのロールを選択し、`ロールを作成`を選択します。
②信頼されたエンティティタイプは`AWS のサービス`を、ユースケースは`Lambda`
を選択します。
③ポリシーのフィルタ欄で `AWSLambdaBasicExecutionRole` と入力して Enter キーを押し、表示されたポリシーを選択状態にします。
![スクリーンショット 2023-11-13 093247.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/b3e69bb6-5aff-503a-e502-4edadd580646.png)
④フィルターをクリアし、前の手順と同様に `AmazonDynamoDBFullAccess` と入力して Enter キーを押し、表示されたポリシーを選択します。`次へ`を選択します。
![スクリーンショット 2023-11-13 093520.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/02658545-63cb-62ee-2776-a42bb24976d8.png)
⑤ロール名を `ws-LambdaDynamoRole` と入力します。ここまでの手順で設定した内容が正しいことを確認し、`ロールの作成`を選択します。
![スクリーンショット 2023-11-13 093731.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/90550966-4525-a2dd-8b75-e2320227ae89.png)
⑥`ロール ws-LambdaDynamoRole が作成されました。`と表示されれば成功です。

### 3. Lambda 関数の作成
①AWS コンソール から、AWS Lambda を選択し、`関数の作成`を選択します。
②一から作成を選択し、関数名に `dbDemo`を入力します。ランタイムはNode.js 16.x を選択します。
![スクリーンショット 2023-11-13 094128.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/cd8e4c5e-978b-5882-73bb-d6db8c2b2489.png)
③`デフォルトの実行ロールの変更`を展開し、`既存のロールを使用する`を選択します。既存のロールドロップダウンリストで、上記の IAM で作成したロールを選択し、`関数の作成`を選択します。
![スクリーンショット 2023-11-13 094348.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/0fa75a82-4c4e-6ec8-4332-2ec660e457d7.png)
④下記の Javascript サンプルコード をコピーし、コードセクションの Lambda コードに貼り付けます。Deploy を選択し、関数を保存します。
<details><summary>Javascript サンプルコード</summary>


```
/*
MIT No Attribution

Copyright 2021 Amazon Web Services, Inc. or its affiliates. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

// Get the AWS SDK and Dynamo SDKs ready
var AWS = require("aws-sdk");
var docClient = new AWS.DynamoDB.DocumentClient();

//Sets the timezone environment variable for the Lambda function
process.env.TZ = "Asia/Tokyo";

exports.handler = (event, context, callback) => {
    // set the options for the date format
    var dateOptions = {
        weekday: "short",
        year: "numeric",
        month: "short",
        day: "numeric"
    };

    // set the variables to record the current local date and time
    var currentShortDate = new Date().toLocaleDateString();
    var currentLongDate = new Date().toLocaleDateString("ja-JP", dateOptions);
    var currentTime = new Date().toLocaleTimeString("ja-JP");
    var currentTimeStamp = new Date().toString();

    //log the incoming event from Amazon Connect for troubleshooting purposes
    console.log("Received event from Amazon Connect at: " + currentTimeStamp);
    console.log("Amazon Connect Event Details: " + JSON.stringify(event));

    //set the variables for the customer ANI and unique contact ID
    var sourcePhoneNumber = event.Details.ContactData.CustomerEndpoint.Address;
    var currentContactId = event.Details.ContactData.ContactId;

    //set up the database query to be used to lookup customer information from DynamoDB
    var paramsQuery = {
        //DynamoDB Table Name.  Replace with your table name
        TableName: 'connectDemo',
        KeyConditionExpression: "phoneNumber = :varNumber",

        ExpressionAttributeValues: {
            ":varNumber": sourcePhoneNumber
        }
    };

    //set up the database query to be used to update the customer information record in DynamoDB
    var paramsUpdate = {
        //DynamoDB Table Name.  Replace with your table name
        TableName: 'connectDemo',
        Key: {
            "phoneNumber": sourcePhoneNumber
        },

        ExpressionAttributeValues: {
            ":var1": currentTimeStamp,
            ":var2": currentLongDate,
            ":var3": currentTime,
            ":var4": currentContactId,
        },

        UpdateExpression: "SET lastCalledTimeStamp = :var1, lastCalledDate = :var2, lastCalledTime = :var3, lastCalledCallId = :var4"
    };

    //use the lookup query (paramsQuery) we set up to lookup the customer data based on the source phone number from DynamoDB 
    docClient.query(paramsQuery, function (err, dbResults) {
        //check to make sure the query executed correctly, if so continue, if not error out the lambda function
        if (err) {
            console.log(err); // an error occurred
            context.fail(buildResponseFailed);
        }
        //if no error occured, proceed to process the resutls that came back from DynamoDB
        else {
            //log the results from the DynamoDB query
            console.log("DynamoDB Query Results:" + JSON.stringify(dbResults));

            //check to ensure only 1 record came back for the customer phone number
            if (dbResults.Items.length === 1) {

                //set variables for the pertinet information from the returned database record:
                var lastCalledDate = dbResults.Items[0].lastCalledDate;
                var lastCalledTime = dbResults.Items[0].lastCalledTime;
                var customerFirstName = dbResults.Items[0].firstName;
                var customerLastName = dbResults.Items[0].lastName;

                //check to see if there is a record of a previous call, and set the previous call variable accordingly
                var customerFirstCall = true;
                if (lastCalledDate) {
                    customerFirstCall = false
                }

                //update the customer record in the database with the new call information using the paramsUpdate query we setup above:
                docClient.update(paramsUpdate, function (err, data) {
                    if (err) console.log("Unable to update item. Error: ", JSON.stringify(err, null, 2));

                    else console.log("Updated item succeeded: ", JSON.stringify(data, null, 2));

                });

                callback(null, buildResponseNumberFound(customerFirstName, customerLastName, lastCalledDate, lastCalledTime, customerFirstCall));
            } else {
                customerFirstCall = true;
                docClient.update(paramsUpdate, function (err, data) {
                    if (err) console.log("Unable to update item. Error: ", JSON.stringify(err, null, 2));

                    else console.log("Updated item succeeded: ", JSON.stringify(data, null, 2));

                });

                callback(null, buildResponseNumberNotFound(customerFirstCall));
            }

        }
    });
};


//This is the function that will be called on a successful callback if we find the phone number in our database
function buildResponseNumberFound(customerFirstName, customerLastName, lastCalledDate, lastCalledTime, customerFirstCall) {
    var results = {
        firstName: customerFirstName,
        lastName: customerLastName,
        lastCalledDate: lastCalledDate,
        lastCalledTime: lastCalledTime,
        firstCall: customerFirstCall,
        phoneNumberFound: true,
        lambdaResult: "Success"
    };
    console.log("Lambda's Response to Amazon Connect is: " + JSON.stringify(results));
    return results;
}


//This is the function that will be called on a successful callback if we don't find the phone number in our database
function buildResponseNumberNotFound(customerFirstCall) {
    var results = {
        firstCall: customerFirstCall,
        phoneNumberFound: false,
        lambdaResult: "Success"
    };
    console.log("Lambda's Response to Amazon Connect is: " + JSON.stringify(results));
    return results;
}

//This is the function that will be called on an error
function buildResponseFailed() {
    var results = {
        lambdaResult: "Error"
    };
    console.log("Lambda's Response to Amazon Connect is: " + JSON.stringify(results));
    return results;
}
```
</details>

![スクリーンショット 2023-11-13 094641.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/dddf0e79-e330-e4be-2ee5-708e8263e50e.png)

⑤設定セクションで**一般設定**に移動し、`編集`を選択します。
⑥タイムアウト `0分 8秒` に変更し、`保存`を選択します。
![スクリーンショット 2023-11-13 095543.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/668e7421-1cf8-f836-5c98-f422d90ced86.png)

## Step 2: AWS Lambda 統合
### 1. Amazon Connect インスタンスにAWS Lambda関数を追加する
①AWS コンソール から、`Amazon Connect` を開きます。
②インスタンスエイリアス欄から、事前に作成していたインスタンス名を押下します。
③ナビゲーションペインで、`問い合わせフロー`を選択し、AWS Lambda セクションまでスクロールダウンします。Lambda 関数 ドロップダウンを選択し、`dbDemo`を選択します。`Add Lambda Function` を選択します。
④Lambda 関数のリストに 登録した Lambda 関数が表示されることを確認します。
![スクリーンショット 2023-11-13 100314.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/f696f90f-3860-9ea7-0bbf-fc824be8d05b.png)

## Step 3: Amazon Connect ルーティングの設定
### 1. 新しいユーザーの追加
①Amazon Connect インスタンスにログインします。ナビゲーションバーのユーザーから`ユーザー管理`を選択します。
②`新しいユーザーの追加`を選択し、ユーザーを手動で追加が選択されていることを確認します。
![スクリーンショット 2023-11-13 100718.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/404fdc93-f622-9a09-92ad-78f401839a25.png)
③ユーザー情報に新規ユーザーの認証情報で、すべての必須フィールドを規則に従って入力します。
![スクリーンショット 2023-11-13 101334.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/7c90f286-22ff-3dc5-16c1-1ee6c82db6dd.png)
④ルーティングプロファイルで `Basic Routing Profile` を、セキュリティプロファイルで `Agent` を選択します。
![スクリーンショット 2023-11-13 101540.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/46d3c8e2-8861-e8c8-b1f5-d7cc8695e997.png)
⑤完了したら画面右上の`保存`を選択します。

### 2. キューの追加
①ナビゲーションバーからルーティングを選択し、キューを選択します。
②`キューを追加`を選択します。
③名前に `Customer Support` と入力し、オペレーション時間は、`Basic Hours` を選択します。
![スクリーンショット 2023-11-13 102114.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/771b5d1f-005a-f43a-9637-0bc1043310cb.png)
④`保存`を選択します。
⑤前のステップを繰り返して、Password Resetのキューを追加します。※設定値は同じです。
![スクリーンショット 2023-11-13 102330.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/0596eb09-14ce-c0b7-6f0a-aaefb34d233f.png)

### 3. ルーティングプロファイルの編集
①ナビゲーションバーからユーザーを選択し、`ルーティングプロファイル`を選択
②`Basic Routing Profile` を選択します。
③キューセクションで、`キューを追加`を選択します。
![スクリーンショット 2023-11-13 102548.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/56f92871-0fec-9cdb-931b-7a6db278757e.png)
④追加したキューのドロップダウンリストで、`Customer Support` キューを選択します。
- 優先度を 1 に設定します。
- 遅延を 0 に設定します。

⑤再度`キューを追加`を選択し、同じ手順で `Password Reset` キューを追加します。
- 優先度を `1` に設定します。
- 遅延を `0` に設定します。

⑥画面右上の保存を選択します。

### 4. コンタクトフローのインポート
⓪以下の手順を行う前に、こちらをダウンロードしてください
<a href="https://static.us-east-1.prod.workshops.aws/public/d12ec4e5-c408-4b29-be70-a41e141d2e13/static/images/ja-JP/files/AWS_Demo_Flow_Main_J.json" download="AWS_Demo_Flow_Main_J">AWS_Demo_Flow_Main_J</a>
①ナビゲーションバーからルーティングを選択し、`フロー`を選択します。
②`フローの作成`を選択します。
③画面右上の保存の横にあるドロップダウンリストから、`フローのインポート(ベータ)` を選択します。
④`AWS_Demo_Flow_Main_J.json` ファイルを選択し、`インポート` を選択します。
すると、フロー図が自動で生成されました。
![スクリーンショット 2023-11-13 103723.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/4aabf541-2e9a-c38f-4ea8-f9a689ce7021.png)
`⑤画面右上の`公開`を選択してコンタクトフローを保存・公開します。

## Step 4: コンタクトフローのテスト
### 1. 電話番号をコンタクトフローに設定
①Amazon Connect のダッシュボードのナビゲーションバーから、 `チャネル` > `電話番号` を選択します。
②取得済みの電話番号を選択します。ドロップダウンリストから `AWS_Demo_Flow_Main_J コンタクトフロー`を選択し、`保存`を選択します。
![スクリーンショット 2023-11-13 104642.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/23efa4a7-203c-01ca-b5db-4c21908704bb.png)
③取得済みの電話番号にテストコールを行います。
コールすると、 `お客様の電話番号が弊社の記録にありませんでしたので、今後のためにデータベースに追加しました。初めてお電話いただきました。ありがとうございます。` というメッセージが再生され、切断されます。

### 2. DynamoDB テーブルの更新
①AWS コンソール から、Amazon DynamoDB を開きます。
②画面左側のナビゲーションメニューの `テーブル` > `項目を探索` を選択し、リストから `connectDemo` テーブルを選択します。テストコールで使用した電話番号を選択します。
③項目の編集画面が開きます。
- `新しい属性の追加` > `文字列` を選択します。属性名 `firstName` を追加します。値に任意の名前を入力します。
- `新しい属性の追加` > `文字列` を選択します。属性名 `lastName` を追加します。値に任意の姓を入力します。
- `保存して閉じる`を選択します。

![スクリーンショット 2023-11-13 105843.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/2d4ed511-a6f6-0df5-a059-a494c72a01bf.png)
④取得済みの電話番号に再度テストコールを行います。このとき、Amazon Connect はデータベースのテーブルからテストに使用した電話番号を認識し、前の手順で設定した名前と、最後に電話をかけた時間を音声で再生します。
「Shohei Otani様、お電話ありがとうございます。」と応答が来ました！

# Work2: ヘルプデスク Lex ボットの作成
## Step 1: Lex ボットのインポート
### Lex ボットのインポート
⓪以下の手順を行う前に、こちらをダウンロードしてください
<a href="https://static.us-east-1.prod.workshops.aws/public/d12ec4e5-c408-4b29-be70-a41e141d2e13/static/images/ja-JP/files/HelpDesk-Lex_v2_Bot_J.zip" download="HelpDesk-Lex_v2_Bot_J.zip">HelpDesk-Lex_v2_Bot_J.zip</a>
①AWS コンソール から、Amazon Lexを開きます。
②ボットセクションの`アクション` > `インポート`を選択します。
③ボットをインポート画面でボット名に `HelpDesk` と入力します。ファイルを参照を選択し、前の手順でダウンロードした `HelpDesk-Lex_v2_Bot_J.zip `を選択します。
![スクリーンショット 2023-11-13 110542.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/b4bae420-2f14-a79b-e409-e07de104d34e.png)
④以下の情報を設定します。:
- IAM アクセス許可：`基本的な Amazon Lex 権限を持つロールを作成します。` を選択
- 児童オンラインプライバシー保護法 (COPPA)：`いいえ`を選択
- アイドルセッションタイムアウト：`5 分`を設定

⑤`インポート`を選択します。
⑥インポートが成功した通知を確認し、`HelpDesk` ボットを選択します。
![スクリーンショット 2023-11-13 111134.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/09f2346c-b027-3492-e5f2-a9659093907d.png)
⑦画面左側のナビゲーションメニューで `日本語 (JP)` を選択します。画面上のセクションで`Build`を選択して、インポートしたボットの構築処理を開始します。
⑧`ボット (日本語 (JP)) で言語 HelpDesk が正常に構築されました`が表示されれば成功です。

## Step 2: Amazon Connect インスタンスへ Amazon Lex ボットを追加
### 1. Amazon Connectインスタンスへ Amazon Lex ボットを追加
①AWS コンソール から、Amazon Connect へ移動します。選択したリージョンが、Amazon Lex ボットを構築したリージョンと同じであることを確認します。
②インスタンスエイリアスの欄からワークショップに使用するインスタンスの名前を選択します。
③ナビゲーションペインで、`問い合わせフロー`を選択し、Amazon Lex セクションまでスクロールダウンします。
- ボットドロップダウンから `HelpDesk` を選択します。
- エイリアスは `TestBotAlias` を選択します。
- `+ Amazon Lex ボットを追加`を選択して、Amazon Connect のボットリストに追加します。

④Amazon Lex ボットセクションに、ボットが追加されたことを確認します。
![スクリーンショット 2023-11-13 112206.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/7f16c3f3-446e-5bfa-6842-49c604001805.png)

### 2. ヘルプデスクのデモ用フローのインポート
⓪以下の手順を行う前に、こちらをダウンロードしてください
<a href="https://static.us-east-1.prod.workshops.aws/public/d12ec4e5-c408-4b29-be70-a41e141d2e13/static/images/ja-JP/files/AWS_Demo_Flow_HD_J.json" download="AWS_Demo_Flow_Main_HD_J">AWS_Demo_Flow_Main_HD_J</a>
①Amazon Connect ダッシュボードに移動します。
②画面左側のナビゲーションバーから、`ルーティング` > `フロー`を選択します。
③画面右上の`フローの作成` を選択します。
④画面右上の保存の横にあるドロップダウンリストから `フローのインポート (ベータ)` を選択します。
⑤前の手順でダウンロードした `AWS_Demo_Flow_HD_J.json` ファイルを選択し、インポートを選択します。
![スクリーンショット 2023-11-13 113531.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/a9a68677-c44f-6ab0-f46f-caac4c069b3e.png)
フロー図が自動で生成されました。

⑥画面右上の`保存`を選択して、コンタクトフローを保存します。

### 3. コンタクトフローブロックの更新
①コンタクトフロー内の顧客の入力を取得するブロックを開きます。 Amazon Lex セクションまでスクロールします。
- `Lexボットを選択` を選択します。
- 名前ドロップダウンリストから、前の手順で作成した `HelpDesk` ボットを選択します。 
- エイリアスドロップダウンリストで `TestBotAlias` を選択します。
- `保存` を選択します。

![スクリーンショット 2023-11-13 114028.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/9b1164c0-1063-89f4-b6bd-20adb9e0774a.png)

②3つ並んでいる作業キューの選択ブロックのうち、一番上のブロックを開きます。 `Customer Support` キューを選択し、`保存`を選択します。
③前の手順を繰り返し、上から2番目の作業キューの選択ブロックに `Password Reset` キューを、上から3番目の作業キューの選択ブロックに `BasicQueue` キューを選択します。
④画面右上の 公開 を選択し、コンタクトフローを公開します。

### 4. コンタクトフローに電話番号を割り当てテストする
①Amazon Connect ダッシュボードで、画面左側のナビゲーションバーから `チャネル` > `電話番号`を選択します。
②取得した電話番号を選択し、 `AWS_Demo_Flow_HD_J` コンタクトフローを割り当て、`保存`を選択します。
![スクリーンショット 2023-11-13 114422.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3581403/39a325ef-183a-bf92-74be-b38009dabe39.png)
③コンタクトフローの動作を確認するために、取得した電話番号にテストコールをかけます。質問に対し、以下の選択をテストします:
- "PCが起動しません" と言うと、Customer Support キューにルーティングされます。
- "パスワードを忘れました" と言うと、Password Reset キューにルーティングされます。

# まとめ
"パスワードを忘れました" などと言うと、自動音声が対応してくれました。
ただしフロー作成に用いたファイルは複雑で時間がかかりそうだったので、AWS Workshop studioのファイルそのまま使いました。
このファイルをもっと編集するとおもしろそうなのですが、前回の簡易コールセンターに比べてややこしそうなので、今回はここで留めておきます。
データをDynamoDBに保存するのは流用できそうだなと思いました。

# 参照
[AWS Workshop studio](https://catalog.workshops.aws/amazon-connect-lex-integration/ja-JP/2-workshop/300-lex-dynamic "AWS Workshop studio")