LINE Botにお願いしてAmazon Connect APIから電話をかけてもらう

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

事業開発部の酒匂です。最近LINE Botに興味があるので、Amazon ConnectのAPIを使ってLINE Botから電話をかけてみることにしました。

全体図

LINE Botアプリを作成して、LINEからボットへ架電先の電話番号をメッセージ送信すると、LINEプラットフォームからLINE Botアプリがそれを受けて、Amazon Connect APIを呼び出すということをしてみたいと思います。

Amazon Connect APIの実行結果を取得することができるので、成功しても失敗しても、何かしらのメッセージをLINE Botアプリから送信することにします。

準備作業

LINE Botアプリを構築するための準備

下記ブログなどを参考にLINE Botの環境を用意します。

LambdaではじめてのLINE Botを作る

Amazon Connect APIについて

今回使用するAPIは、Outbound APIと呼ばれているものです。下記ブログはプレビュー版がリリースされた際に記載したものですが、現在の仕様と変わっていないので、参考になるかと思います。

Amazon Connect Outbound ContactのPreview版を試してみる

Amazon Connect APIを呼び出すためのAWS ポリシー作成

Amazon ConnectのOutbound APIを呼び出すには、下記のアクションが必要になります。

connect:StartOutboundVoiceContact

下記のアクションについては、通話中のアウトバウンド呼を切断するためのもので、今回は使用しませんがポリシーに含めておくことにします。

connect:StopContact

このようなポリシーを作成し、APIを呼び出すIAMユーザーに付与しておきます。

LINE Botアプリのソースコード作成

今回のサンプルは、PHPフレームワークのLaravelを使って作成しております。必要な部分だけ抜粋いたします。

LINE Bot SDKのダウンロード

composer require linecorp/line-bot-sdk

下記に手順が記載されております。

  • line-bot-sdk-php

https://github.com/line/line-bot-sdk-php

AWS SDK for PHPのダウンロード

composer require aws/aws-sdk-php

下記に手順が記載されております。

  • Composer を使用したインストール

https://docs.aws.amazon.com/ja_jp/sdk-for-php/v3/developer-guide/getting-started_installation.html

LineBotController.php

LINEプラットフォームからのコールバックを受けて、実行したい処理を記載します。

Amazon Connect APIを使って電話をかける際の条件としては、「受信したメッセージが+81で始まる場合」としておりますので、ちゃんと作る場合は電話番号チェックを入れる必要があると思います。

  • 71行目の「ContactFlowId」に指定できるフローの種類は「問い合わせフロー(Contact Flow)」のみです。
  • 72行目の「DestinationPhoneNumber」と75行目の「SourcePhoneNumber」は+815012345678の形式(E.164形式)で設定する必要があります。
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;

class LineBotController extends Controller
{
    /**
     * LINE Message Platformからのコールバックを受ける
     *
     * @param  
     * @return 
     */
    function callback(Request $request)
    {
	    // X-Line-Signatureリクエストヘッダーに含まれる署名を検証する
        $channelId = '{LINEのCHANNEL ID}';
        $channelSecret = '{LINEのCHANNEL SECRET}';
        $lineAccessToken = '{LINEのACCESS TOKEN}';

        $httpRequestBody = $request->getContent();
        $hash = hash_hmac('sha256', $httpRequestBody, $channelSecret, true);
        $signature = base64_encode($hash);
        $xLineSignature = $request->header('X-Line-Signature');

        // 署名検証に成功
        if($signature === $xLineSignature){
            Log::info('署名検証に成功した');

            // Line Botの生成
            $httpClient = new \LINE\LINEBot\HTTPClient\CurlHTTPClient($lineAccessToken);
            $bot = new \LINE\LINEBot($httpClient, ['channelSecret' => $channelSecret]);

            // イベント内容を処理する
            $eventData = json_decode($httpRequestBody);
            foreach ($eventData->events as $event) {
                // メッセージイベント
                if($event->type === "message"){
                    Log::info('イベント発生:メッセージイベント' . $httpRequestBody);
                    $replyToken = $event->replyToken;

                    // LINE プラットフォームからの接続確認の場合は200を返す
                    if($replyToken === "00000000000000000000000000000000"){
                        return response(' ', 200);
                    }

                    // メッセージが+81で始まる場合
                    if(strpos($event->message->text,'+81') !== false){
                        
                        // Amazon Connectのクライアント作成
                        $credentials = new \Aws\Credentials\Credentials(
                            '{AWS IAMユーザーのAccess Key ID}', 
                            '{AWS IAMユーザーのSecret Access Key}'
                        );
                        
                        $client = new \Aws\Connect\ConnectClient([
                            'version'     => 'latest',
                            'region'      => 'ap-southeast-2',
                            'credentials' => $credentials   
                          ]);
                          
                        // Amazon Connect Outbound APIで発信する
                        $result = $client->startOutboundVoiceContact([
                            'Attributes' => [
                                // プロンプトで再生させるメッセージ
                                'message' => "頼まれたので電話をしました。"
                            ],
                            'ClientToken' => null,
                            'ContactFlowId' => '{Amazon ConnectのコンタクトフローID}', // REQUIRED
                            'DestinationPhoneNumber' => $event->message->text, // REQUIRED
                            'InstanceId' => '{Amazon ConnectのインスタンスID}', // REQUIRED
                            'QueueId' => '{Amazon ConnectのキューのID}',
                            'SourcePhoneNumber' => '{Amazon Connectで発信した際の発信者番号}'
                        ]);

                        // 処理が成功
                        if($result['@metadata']['statusCode'] === 200){
                            Log::info('connect API成功!');
                            // LINE Botからメッセージを返す
                            $response = $bot->replyText($replyToken, "contactId = " . $result['ContactId'] . " で承りました!");
                        }
                        // 処理が失敗
                        else{
                            Log::info('connect APIエラー!');
                            // LINE Botからメッセージを返す
                            $response = $bot->replyText($replyToken, "エラーが発生しました。");
                        }
                    }
                    else{
                        Log::info('入力電話番号が不正!');
                        // LINE Botからメッセージを返す
                        $response = $bot->replyText($replyToken, "電話番号を教えてください。");
                        
                    }
                }				
            }
            return response(' ', 200);
        }
        // 署名検証に失敗
        else{	
            Log::info('署名検証に失敗した');
            return response(' ', 200);
        }
	}
}

VerifyCsrfToken.php

Laravelの仕様で、POSTの場合はCSRFトークンの送信が必須となりますが、LINEプラットフォームからは受け取れないので、コールバックを受けるエンドポイントについては除外しておきます。

<?php

namespace App\Http\Middleware;

use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;

class VerifyCsrfToken extends Middleware
{
    /**
     * The URIs that should be excluded from CSRF verification.
     *
     * @var array
     */
    protected $except = [
        //
        'https://{LINEプラットフォームからのコールバックを受けるパス}'
    ];
}

web.php

LINEプラットフォームからのコールバックを受けるためのルーティング設定をしておきます。

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::post('/callback', 'LineBotController@callback');

Amazon Connectのコンタクトフロー

LineBotController.phpの71行目の「ContactFlowId」に指定するフローです。

「問い合わせ属性の設定」ブロックでAmazon Connect APIを呼び出す際に指定したAttribute(コンタクト属性)の値を取得します。取得した値をプロンプトの再生で使っております。

Amazon Connect APIを呼び出す際に指定したAttribute(コンタクト属性)の値を取得は下記のようになります。API側で設定した値なので、「ユーザー定義」になります。(Lambda関数で設定した値だと、「外部」になりますので、勘違いするとハマると思いました。)

取得したAttribute(コンタクト属性)の値をプロンプト再生で使用します。

動作確認

こんな感じで、電話番号だけをボットに伝えるという簡素なものでありますが、ボットがメッセージを返すのと同じぐらいのタイミングで、電話がかかってきて、APIを呼び出す際に設定した「頼まれたので電話をしました」という、なんともやる気のないメッセージがMizukiさんの声によって再生されます。

おわりに

今回はLINE BotからAmazon ConnectのAPIを呼び出して電話をかけておりますが、要するにこれはシステム障害が発生した際に、(メールやチャットツールへの通知に加えて)電話での自動連絡を行うといった用途に使えると思います。

システム障害の電話連絡を行う作業負荷はそれなりにあると思いますし、人が電話をかけて伝えるよりは、システムが淡々と伝える方が良い場合もあると思います。

あとは、ボットに「◯時◯分に起こして」みたいにメッセージを送って、モーニングコールをしてもらうといった使い方もあるかと思います。

参考