はじめに
この記事は、Twilio Flex Advent Calendar 2021の22日目の記事です。
普段、AWS周りをよくさわることが多いのですが、今回はTwilioに挑戦してみます。そして、Twilio Flexはじめて知りました。
とても面白そうなサービスだと思い、また簡単に無料トライアルを開始することできたので、今回、Twilio FlexとAWSのサービスを連携する系のブログを書きたいと思います。
やりたいこと
電話をかけてきたユーザがVIPユーザなのか、そうでないのかDynamoDBのテーブルに保存してあるユーザ情報をもとに判定して、Twilio Flexの着信画面に表示したい
概要図
今回、構築するシステムの全体像はこんなかんじです。
TwilioとAWSとの連携はAPI Gatewayを使って連携します。
Twilio FunctionsからAPI Gateway + Lambda + DynamoDBで構築したAPIを実行し架電したユーザ情報を取得、Functionsで受け取ったレスポンス値からVIPか判定し、Flex画面に表示する流れとなります。
(フロー作成するStudioから直接APIを実行できそうではあったのですが、今回、柔軟性が高いTwilio Functionsを利用しています。)
前提
-
AWSアカウントは発行済みで利用できる状態になっている
-
IAMユーザの設定がされている状態になっている
-
AWS CLIなど利用できるようにアクセスキー、クレデンシャルの設定がされている状態になっている
-
Serverless FrameworkがインストールできるNode.js環境がある
Twilio Flexをセットアップ
下記ブログを参考にセットアップしていきます。
ログイン後、「Explore Products」 => 「All Products」 => SolutionsのFlexを選択
「Create my Flex account」をクリック
アカウント名を入力し、Verifyをクリック
数分でFlex環境が作成されます。
Flexの画面が表示されたら、右上にある「COMPLETE」をクリックしていきタスクを完了しておきます。
また、右上を確認すると、自分自身の状態がAvailableになっていることがわかります。
これは自分自身が有効なワーカーとしてログインしているということになります。
Availableの状態だと課金されて続けるので、状態をオフラインにしておきます。そうすれば、Flexの課金が止まります。
利用していないときは適宜オフラインにしておきましょう。
オフラインの方法は右上の自分自身のアイコン付近をクリックして、プルダウンメニューから「Offline」を選択することでオフラインにできます。
着信のテスト
下記ブログを参考に着信のテストを行っておきましょう。より理解が深まるとかと思います。
APIGateway + Lambda + DynamoDBを使って簡易APIを実装
Twilio Flexのセットアップが完了したので、次はAPIの実装を行います。
DynamoDBに保存されているユーザ情報が取得する簡易なAPIを実装します。
サンプル実装なので認証はAPIキーで行います。
例としては、下記のようにAPIを実行するとvipかどうか情報が取得できるAPIを実装する想定です。
$ curl -X GET "<endpoint>/dev/users/<id>" -H "x-api-key: XXXXXXXXXXXXXXX"
{"ID": "<id>", "vip": true}
また、今回は手順を簡単にするためServerless Frameworkを使いデプロイします。
環境
-
Node.js: v16.13.0
-
Lambdaのランタイム: python 3.8
$ serverless --version
Framework Core: 2.69.1
Plugin: 5.5.1
SDK: 4.3.0
Components: 3.18.1
Serverless Framework をインストール
$ npm install serverless -g
プロジェクトを作成
$ serverless create --template aws-python3
雛形作成されたら、ファイルを編集していきます。
handler.pyを下記のように編集します。
import json
import boto3
from botocore.exceptions import ClientError
def hello(event, context):
print(event)
id = event["pathParameters"]["id"]
body = {
"ID": id,
"vip": False
}
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table("test-table")
response = {
"statusCode": 200,
"body": json.dumps(body)
}
try:
res = table.get_item(Key={"ID": str(id)})
print(res["Item"])
response["body"] = json.dumps(res["Item"])
print(response)
return response
except KeyError:
return response
except ClientError as e:
print(e.response["Error"]["Message"])
DynamoDBにデータがなければ、vipの値がfalseが、データがあれば上書きして返すようにしています。
「test-table」というテーブル名でDynamoDBのテーブルを作成する予定なので、そのテーブル名を指定しています。
サンプル実装ですので本番環境での利用はお控えください。
serverless.ymlを下記のように編集します。
service: twilio-sample
frameworkVersion: "2"
provider:
name: aws
runtime: python3.8
lambdaHashingVersion: 20201221
apiGateway:
apiKeys:
- testKey
usagePlan:
quota:
limit: 1000
offset: 2
period: MONTH
throttle:
burstLimit: 200
rateLimit: 100
iam:
role:
statements:
- Effect: Allow
Action:
- dynamodb:GetItem
Resource:
- "*"
functions:
hello:
handler: handler.hello
events:
- http:
private: true
path: users/{id}
method: get
request:
parameters:
paths:
id: true
# DynamoDBのテーブルを作成
resources:
Resources:
StateTable:
Type: "AWS::DynamoDB::Table"
Properties:
AttributeDefinitions:
- AttributeName: ID
AttributeType: S
KeySchema:
- AttributeName: ID
KeyType: HASH
BillingMode: PAY_PER_REQUEST
TableName: test-table
サンプル実装なのでLambda関数は「hello」というデフォルトの関数名のままにしています。適宜変更ください。
「test-table」というテーブル名でDynamoDBのテーブルが作成されます。
デプロイ
AWSのプロファイルをデプロイ先アカウントに向け、デプロイします。
下記コマンドを実行し、リソースの情報など出力されたら成功です!
$ sls deploy
Serverless: Stack update finished...
Service Information
service: twilio-sample
stage: dev
region: us-east-1
stack: twilio-sample-dev
resources: 17
api keys:
testKey: xxxxxxxxxxxxxxxxx
endpoints:
GET - https://xxxxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/users/{id}
functions:
hello: twilio-sample-dev-hello
layers:
None
出力されたapi keyとendpointを控えておきます。
DynamoDBにデータ保存
次は、バージニアリージョンに作成されたDynamoDBのテーブルにデータを追加します。
AWSのマネージメントコンソールからDynamoDBのサービスページに移動し、作成されたテーブル「test-table」を選択します。
追加するデータのIDは今回、電話番号になります。「+81」を先頭につけて、電話番号の先頭0を削った形で値を設定してください。
例: 090xxxxyyyy のとき +8190xxxxyyyy を値に設定
次に「新しい属性の追加」でboolを選択し、属性名を「vip」とし、値を「True」にして保存してください。
APIの動作確認
curlで動作確認してみます。
想定通りのレスポンスが返ってこればOKです。
DynamoDBに登録していないIDでリクエストするとvipがfalseになってかえってくることも確認しておきましょう。
$ curl -X GET "https://xxxxxxxxxxxx.execute-api.us-east-1.amazonaws.com/dev/users/+8190xxxxyyyy" -H "x-api-key: XXXXXXX"
{"ID": "+8190xxxxyyyy", "vip": true}%
Twilio Functionsの設定
API側の準備もできたので、次はTwilio Flexと連携するためのTwilio Functionsの設定を行っていきます。
フロー作成するStudioにHTTPをコールするMake HTTP Requestがありますが、ヘッダーの設定ができない?ようだったので、より柔軟なTwilio Functionsを今回利用します。
Twilio Functions は
Twilio ログイン後、「Explore Products」 => 「All Products」 => Developer toolsのFunctionsを選択すると表示されます。
ピン留めされていなければしておきましょう。
Servicesは今回「test」として作成していきます。適宜変更ください。
Functionsから外部APIを実行する
今回、手順簡略化するために、はじめからある/welcome
を編集する形で進めます。
下記のように編集します。
const axios = require('axios');
exports.handler = async (context, event, callback) => {
const apiKey = context.API_KEY;
const endpoint = context.ENDPOINT;
const tel = event['data'];
const { data } = await axios.request({
url: `${endpoint}/users/${tel}`,
method: 'GET',
headers: {
'X-Api-Key': apiKey
}
});
let message = '';
if(data.vip) {
message = 'vip'
}
return callback(null, {
message: message
});
};
処理の流れを簡単に解説すると
- Functionに入力されるeventに含まれる、電話番号情報を取得
- 先程実装した、APIを実行
- APIのレスポンスに含まれるvipがTrueならmessageにvipをいれる
- callbackでmessageを返す
という処理を行っています。
環境変数を設定する
API Keyやエンドポイントはコードに埋め込みはせずに環境変数から取得するようにするため、Environment Variablesに設定します。
今回、API_KEY
とENDPOINT
を設定しています。
ENDOPOINT
にはhttps://xxxxxxx.execute-api.us-east-1.amazonaws.com/dev
という形で値を登録しています。
ここで設定した値はcontextから値を取得できます。
contextのサンプル
{
PATH: '/welcome',
AUTH_TOKEN: 'xxxxxxxxxxxxxxxxxxxxxxxxxx',
SERVICE_SID: 'xxxxxxxxxxxxxxxxxxxxxxxxxx',
API_KEY: 'xxxxxxxxxxxxxxxxxxxxxxxxxx',
ENVIRONMENT_SID: 'xxxxxxxxxxxxxxxxxxxxxxxxxx',
ENDPOINT: 'https://xxxxxxx.execute-api.us-east-1.amazonaws.com/dev',
ACCOUNT_SID: 'xxxxxxxxxxxxxxxxxxxxxxxxxx',
DOMAIN_NAME: 'xxxxxx.twil.io',
getTwilioClient: [
Function
]
}
Twilio Functionsで利用するライブラリを追加
functions内で利用するライブラリはDependenciesから追加することで利用できるようになります。
今回、APIをリクエストするためにaxiosを追加しています。
デプロイ
saveを押して、Deploy Allボタンを押すことで反映されます。
Twilio Studio でフローを編集する
Twilio Studioが左ペインになければ、
ログイン後、「Explore Products」 => 「All Products」 => Developer toolsのStudioを選択するとアクセスできます。
ピン留めされていなければしておきましょう。
Voice IVRを編集
デフォルトで用意されているものがあるので、それを編集していきます。
着信したら実行されるフローが「Vocie IVR」になります。
Twilio Functionsをフローに挟むために、Run Functionを追加します。
function_1の設定は下記のようにします。
Function Parametersにdata,**{{trigger.call.From}}**として追加しておきます。
**{{trigger.call.From}}**は着信があった電話番号になります。
ここで設定した値がeventsに入ってきます。
設定できたら、Saveボタンを押します。
Flexの着信画面にVIPかどうか表示させる
FlexのUIはカスタマイズすることが可能です。
ですが、プラグイン開発が必要になったりするため、今回は既存仕組みを編集する形で対応します。
着信画面にデータを渡しているのはSendCallToAgentの「Send To Flex」Widgetを編集して対応
StudioからTaskRouter(着信画面など)にコールを引き渡す役目をするのが、「Send To Flex」Widgetになります。
ここでは、すでにSendCallToAgentとして実装されていますので、こちらを少し修正して着信画面にVIPかどうか表示させたいと思います。
つまり、SendCallToAgentのATTRIBUTESを下記のように修正します。
{ "type": "inbound", "name": "({{widgets.function_1.parsed.message}}) {{trigger.call.From}}" }
現在、nameパラメータを使うことで、Flexの着信画面に電話番号が表示されるようになっています。
そこにFunctionから受け取った値({{widgets.function_1.parsed.message}})を追加しています。
設定を反映
修正したフローをTwilio Studioに反映させるためには、最後にパブリッシュする必要があります。
画面上部にあるPublishボタンを押します。
動作確認
DynamoDBに登録されている電話番号からFlexに電話をかけてみます。
すると、想定通りVIPと表示されました!
DynamoDBのデータを消して、再度着信してみると、今度はVIPが表示されなくなりました!
もし、うまくいかなければAWS側のCloudWatch Logsに出力されるLambdaのログを確認するとよいかと思います。
AWS環境の片付け
検証後のお片付け
フルマネージドサービスを利用しているので、時間で課金されることはないですが削除するには
下記コマンドを実行することでAWSのリソースを削除することができます。
$ sls remove
参考サイト