API Gateway + LambdaでLINE Bot開発

こんにちは。katoです。

今回はAWSサービスを利用したLINE Botの開発を行っていきたいと思います。

 

概要

LINE BotはLINE Developersアカウントを所有していれば簡単に作成することが可能です。

今回はDeveloper Trialのプランを利用して、簡易的なLINE Botを作成します。

 

手順

LINE Developers

まず初めにLINE Developers側のセットアップを行います。

LINEのアカウントを所有している場合には、LINEアカウントを利用してLINE Developersを開始することが可能です。

LINEを起動し、「ホーム」→「設定」→「アカウント」を開き、「メールアドレス」と「パスワード」を設定します。

設定したメールアドレス宛に認証番号が届きますので、LINE側でこの番号を入力して登録を完了します。

LINE Developersにて開発を行っていきます。

下記URLにアクセスし、「ログイン」をクリックします。

 

https://developers.line.biz/ja/

 

「LINEアカウントでログイン」を選択し、先程設定したメールアドレスとパスワードでログインします。

開発者登録を求められるので適当に入力して次に進みます。

プロバイダーを作成します。
プロバイダー名を入力するのみなので好きな名前で作成して下さい。

次にチャネルを作成します。

今回はBotを作成するので「Messaging API」を選択し、チャネルを作成していきます。

設定は適宜入力していくのみなので簡単ですが、「line」といった文字がアプリ名に含まれるとエラーでチャネルが作成できないようので、ここだけ注意してください。

作成したチャネルを開き、設定を行っていきます。

「メッセージ送受信設定」の個所の設定が少しだけ必要です。

まず、「アクセストークン」にて「再発行」をクリックし、アクセストークンを発行します。

このアクセストークンはLambdaからLINEに対してメッセージを送る際に利用するので控えておきます。

次に、「Webhook送信」を「利用する」に設定します。

「Webhook URL」はAPI Gatewayのエンドポイントを指定するので、今は設定不要です。

その他の設定は任意となりますので、適宜用途などに合わせて設定してください。

 

AWS

ここからAWS側の設定に移っていきます。

まずLambda関数を作成します。

今回はPython 3.6にて関数の作成を行っています。

import json
import boto3
import urllib
import re
import os

def lambda_handler(event, context):
    print (event)
    url = "https://api.line.me/v2/bot/message/reply"
    method = "POST"
    headers = {
        'Authorization': os.environ['CHANNEL_ACCESS_TOKEN'],
        'Content-Type': 'application/json'
    }
    if (event['events'][0]['type'] == "postback"):
        if (event['events'][0]['postback']['data'] == "blog"):
            message = [
                {
                    "type": "text",
                    "text": "ブログをお探しします!",
                    "quickReply": {
                        "items": [
                            {
                                "type": "action",
                                "action": {
                                    "type": "postback",
                                    "label": "おすすめ",
                                    "data": "Recommended",
                                    "displayText": "おすすめ"
                                }
                            },
                            {
                                "type": "action",
                                "action": {
                                    "type": "postback",
                                    "label": "キーワード検索",
                                    "data": "keyword",
                                    "displayText": "キーワード検索"
                                }
                            }
                        ]
                    }
                }
            ]
        elif (event['events'][0]['postback']['data'] == "Recommended"):
            message = [
                {
                    "type": "text",
                    "text": "おすすめはこちら!"
                },
                {
                    "type": "text",
                    "text": "https://xp-cloud.jp/blog/news/"
                }
            ]
        elif (event['events'][0]['postback']['data'] == "service"):
            message = [
                {
                    "type": "text",
                    "text": "どのようなサービスをお探しでしょうか?",
                    "quickReply": {
                        "items": [
                            {
                                "type": "action",
                                "action": {
                                    "type": "postback",
                                    "label": "AWS導入",
                                    "data": "AWS",
                                    "displayText": "AWS"
                                }
                            },
                            {
                                "type": "action",
                                "action": {
                                    "type": "postback",
                                    "label": "Backup",
                                    "data": "Backup",
                                    "displayText": "Backup"
                                }
                            },
                            {
                                "type": "action",
                                "action": {
                                    "type": "postback",
                                    "label": "AWS移行",
                                    "data": "Migration",
                                    "displayText": "Migration"
                                }
                            },
                            {
                                "type": "action",
                                "action": {
                                    "type": "postback",
                                    "label": "アプリ開発",
                                    "data": "app",
                                    "displayText": "app"
                                }
                            },
                            {
                                "type": "action",
                                "action": {
                                    "type": "postback",
                                    "label": "監視・運用",
                                    "data": "operation",
                                    "displayText": "operation"
                                }
                            },
                            {
                                "type": "action",
                                "action": {
                                    "type": "postback",
                                    "label": "VPN・専用線",
                                    "data": "VPN",
                                    "displayText": "VPN"
                                }
                            },
                            {
                                "type": "action",
                                "action": {
                                    "type": "postback",
                                    "label": "鳴子",
                                    "data": "NARUKO",
                                    "displayText": "NARUKO"
                                }
                            }
                        ]
                    }
                }
            ]
        elif (event['events'][0]['postback']['data'] == "AWS"):
            message = [
                {
                    "type": "text",
                    "text": "サービスサイトにご案内します!"
                },
                {
                    "type": "text",
                    "text": "https://xp-cloud.jp/service/#aws_consul"
                }
            ]
        elif (event['events'][0]['postback']['data'] == "Backup"):
            message = [
                {
                    "type": "text",
                    "text": "サービスサイトにご案内します!"
                },
                {
                    "type": "text",
                    "text": "https://xp-cloud.jp/service/#cloud"
                }
            ]
        elif (event['events'][0]['postback']['data'] == "Migration"):
            message = [
                {
                    "type": "text",
                    "text": "サービスサイトにご案内します!"
                },
                {
                    "type": "text",
                    "text": "https://xp-cloud.jp/service/#cloudmigration"
                }
            ]
        elif (event['events'][0]['postback']['data'] == "app"):
            message = [
                {
                    "type": "text",
                    "text": "サービスサイトにご案内します!"
                },
                {
                    "type": "text",
                    "text": "https://xp-cloud.jp/service/#application"
                }
            ]
        elif (event['events'][0]['postback']['data'] == "operation"):
            message = [
                {
                    "type": "text",
                    "text": "サービスサイトにご案内します!"
                },
                {
                    "type": "text",
                    "text": "https://xp-cloud.jp/service/#network"
                }
            ]
        elif (event['events'][0]['postback']['data'] == "VPN"):
            message = [
                {
                    "type": "text",
                    "text": "サービスサイトにご案内します!"
                },
                {
                    "type": "text",
                    "text": "https://xp-cloud.jp/service/#monitoring"
                }
            ]
        elif (event['events'][0]['postback']['data'] == "NARUKO"):
            message = [
                {
                    "type": "text",
                    "text": "サービスサイトにご案内します!"
                },
                {
                    "type": "text",
                    "text": "https://xp-cloud.jp/naruko/"
                }
            ]
        elif (event['events'][0]['postback']['data'] == "keyword"):
            message = [
                {
                    "type": "text",
                    "text": "「検索」の後にスペースを入れてキーワードを入力してください。\n\n例)検索 DynamoDB\n\nキーワードにスペースが含まれる場合にはAND検索となります。"
                }
            ]
    elif (event['events'][0]['type'] == "message"):
        if (event['events'][0]['message']['text'] == "案内"):
            message = [
                {
                    "type": "text",
                    "text": "こんにちは!ご用件をお伺いできますでしょうか?",
                    "quickReply": {
                        "items": [
                            {
                                "type": "action",
                                "action": {
                                    "type": "postback",
                                    "label": "blog",
                                    "data": "blog",
                                    "displayText": "blog"
                                }
                            },
                            {
                                "type": "action",
                                "action": {
                                    "type": "postback",
                                    "label": "service",
                                    "data": "service",
                                    "displayText": "service"
                                }
                            }
                        ]
                    }
                }
            ]
        elif (re.match("検索", event['events'][0]['message']['text'])):
            keywords = event['events'][0]['message']['text'].split()
            if (len(keywords) > 1):
                keywords.remove("検索")
                keyword = "+".join(keywords)
                result = "https://xp-cloud.jp/blog/?s=" + str(keyword)
                message = [
                    {
                        "type": "text",
                        "text": "検索結果をご案内します。"
                    },
                    {
                        "type": "text",
                        "text": result
                    }
                ]
            else:
                message = [
                    {
                        "type": "text",
                        "text": "入力を正しく処理できませんでした。\n「検索」の後にスペースを入れてキーワードを入力してください。\n\n例)検索 DynamoDB"
                    }
                ]
        elif (event['events'][0]['message']['text'] == "こんにちは"):
            message = [
                {
                    "type": "text",
                    "text": "こんにちは!"
                }
            ]
        else:
            message = [
                {
                    "type": "text",
                    "text": "ご利用の際には「案内」と入力してください。"
                }
            ]
    else:
        message = [
            {
                "type": "text",
                "text": "申し訳ございません。ご入力を正しく受け取ることができませんでした。お手数ですが再度ご入力いただけますでしょうか。"
            }
        ]
    params = {
        "replyToken": event['events'][0]['replyToken'],
        "messages": message
    }
    request = urllib.request.Request(url, json.dumps(params).encode("utf-8"), method=method, headers=headers)
    with urllib.request.urlopen(request) as res:
        body = res.read()
    return 0    

 

サイト案内をするだけの簡単なBotとなっております。

今回は顧客入力のパターンマッチによって処理するBot機能は最小限に抑え、quickReplyと呼ばれる選択肢タイプのBotを作成しています。

LINE Botでは顧客入力とquickReplyでの入力でイベントが少し異なるので注意が必要です。

また、今回は顧客入力に応答するタイプのBotとなりますので、イベントに含まれる「replyToken」を利用してLINEに対してメッセージを返しています。

LINE連携部分以外は簡単なコードとなっているので、詳細は省略させていただきますが、前半に指定している「Authorization」の指定だけ注意してください。

「Authorization」にはLINEチャネル設定時に発行したアクセストークンを指定するのですが、そのままアクセストークンを指定しても正常に動きませんでした。

下記のようにアクセストークンの先頭に「Bearer  」を指定する必要があるようなので注意して下さい。

 

Bearer <アクセストークン>

 

Lambda関数の作成が完了したらAPI Gatewayを作成していきます。

一般的なLambda連携のPOSTメソッドを作成するのみなのです、手順は省略しますが、リソースの作成だけ少し注意してください。

リソースとして「/」を指定している場合には、LINEからのイベントが正しく送られてきません。

理由は不明ですが「/bot」といったように「/」以外のリソースを作成して、そこにPOSTメソッドを作成してあげる必要があります。

設定が完了したらAPIをデプロイします。

呼び出しURLを先程のLINE Developersの「Webhook URL」に指定すれば設定完了です。

 

動作確認

「案内」と入力することでquickReplyの選択肢が表示されます。

quickReply

 

後は案内に従って選択、入力を行います。

 

なお。顧客側の入力欄に選択した内容が表示されていますが、これはquickReplyの「displayText」を設定しているためです。

quickReplyの「displayText」

 

quickReplyの「displayText」

 

quickReplyではあらかじめ決められた内容しか設定できないため、顧客入力に応じて処理する場合には特定の文字を入力に含めるなどの対応が必要となります。

 

quickReply

 

まとめ

API GatewayとLambdaを利用したLINE Botの開発をご紹介しました。

LINE Botは簡単に開発することができますが、AWSと連携することでBotとしての機能をさらに拡張していくことが可能になるかと思います。

Amazon Rekognitionと連携した画像認識であったり、Amazon Personalizeと連携したユーザ個別のリアルタイムパーソナライゼーションなど、ユニークで実用的な機能をAWSサービスを利用して実現することが可能になります。

次回は他のAWSサービスも連携して、もう少し複雑なBotを作ってみたいと思います。

 

 

 

このブログの著者

 

 

アプリケーション開発バナー   AWS相談会