16
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

API GatewayとLambda(Python)でLINE BOT(Messaging API)開発 [後編]

Last updated at Posted at 2021-01-04

API GatewayとLambda(Python)でLINE BOT(Messaging API)開発 [後編]

はじめに

前編では主に環境構築と、「こんにちは!」と返すだけの簡単なサンプルを載せました。
後編となる本記事では、line-bot-sdkを利用して以下のことを実装してみたいと思います。
・文字列の応答
・ユーザー情報の取得
・選択肢の提示、選ばれたものの取得
・位置情報の受信、位置情報の送信

line-bot-sdkのインストール

Lambdaでデプロイする前提ですので、lambda_function.pyと同じ場所にインストールする必要があります。
インストールしたライブラリ類をまとめてzip圧縮できるよう、適当なフォルダ(ここではsource)の中で行いましょう。

|- source
   |- lambda_function.py

ライブラリはpipでインストールします。

$ cd source
$ pip install line-bot-sdk -t .

Lambdaへのデプロイ

sourceフォルダの中身をzip圧縮して、そのzipファイルをlambdaへデプロイします。

$ cd source
$ zip -r ../lambda-package.zip *
$ aws lambda update-function-code --function-name sample-map-function-scraping --zip-file fileb://../lambda-package.zip

(※ sample-map-function-scrapingは目的のLambda関数名に変えてください。)

Lambdaコンソールの関数コードを確認すると、ライブラリも含まれた状態でデプロイされていることが確認できますね。
image.png

環境変数にチャンネルシークレットを設定する

前編では使いませんでしたが、LINE Developpersで作成したチャンネルの「チャンネル基本設定」にあるチャネルシークレット を使うことになります。環境変数にLINE_CHANNEL_SECRETというキーの値として登録しておいてください。

文字列を応答する

前編では line-bot-sdk を用いず「こんにちは!」を応答しました。
line-bot-sk を利用した場合にどうなるか、比較のために書いてみます。

lambda_function.py
import os
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage
)
LINE_CHANNEL_ACCESS_TOKEN   = os.environ['LINE_CHANNEL_ACCESS_TOKEN']
LINE_CHANNEL_SECRET         = os.environ['LINE_CHANNEL_SECRET']
LINE_BOT_API = LineBotApi(LINE_CHANNEL_ACCESS_TOKEN)
LINE_HANDLER = WebhookHandler(LINE_CHANNEL_SECRET)

def lambda_handler(event, context):
    logger.info(event)
    signature = event["headers"]["x-line-signature"]
    body = event["body"]
    
    @LINE_HANDLER.add(MessageEvent, message=TextMessage)
    def on_message(line_event):
        LINE_BOT_API.reply_message(line_event.reply_token, TextSendMessage("こんにちは!"))
    
    LINE_HANDLER.handle(body, signature)
    return 0

少しすっきりした感じがしますね。
ライブラリを利用した書き方に慣れていないと(知っていないと) 難しく感じるかもしれませんが、使っていくうちに慣れると思います。

ユーザー情報

LINEのメッセージから取得可能なユーザー情報はどんなものがあるのでしょうか。
それを確認してみたいと思います。

    @LINE_HANDLER.add(MessageEvent, message=TextMessage)
    def on_message(line_event):
        profile = LINE_BOT_API.get_profile(line_event.source.user_id)
        logger.info(profile)
        LINE_BOT_API.reply_message(line_event.reply_token, TextSendMessage("こんにちは!"))

profileの内容をログ出力して確認してみると、以下のようなJSONとなっています。

{
    "displayName": "あなたのLINE名",
    "language": "ja",
    "pictureUrl": "https://profile.line-scdn.net/ch/v2/p/uxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/0000000000000",
    "userId": "U00000xxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

LINEに登録している名前、プロフィール画像のURL、ユーザーIDなどを取得することができました。

選択肢

選択肢を提示したり、選ばれたものを取得するための実装サンプルです。
・「選択肢」というメッセージを受け取ったら、いくつかの選択肢を応答する。
・選ばれたら「{選ばれたタイプ} + を 選択しましたね!」と応答する

lambda_function.py
import os
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,
    TemplateSendMessage, ButtonsTemplate,
    PostbackEvent
)
LINE_CHANNEL_ACCESS_TOKEN   = os.environ['LINE_CHANNEL_ACCESS_TOKEN']
LINE_CHANNEL_SECRET         = os.environ['LINE_CHANNEL_SECRET']
LINE_BOT_API = LineBotApi(LINE_CHANNEL_ACCESS_TOKEN)
LINE_HANDLER = WebhookHandler(LINE_CHANNEL_SECRET)

def lambda_handler(event, context):
    logger.info(event)
    signature = event["headers"]["x-line-signature"]
    body = event["body"]

    # テキストメッセージを受け取った時に呼ばれるイベント
    @LINE_HANDLER.add(MessageEvent, message=TextMessage)
    def on_message(line_event):
        # ユーザー情報を取得する
        profile = LINE_BOT_API.get_profile(line_event.source.user_id)
        logger.info(profile)

        message = line_event.message.text.lower()
        if message == '選択肢':
            LINE_BOT_API.reply_message(line_event.reply_token, make_select_message())
        else:
            LINE_BOT_API.reply_message(line_event.reply_token, TextSendMessage("こんにちは!"))

    # 選択肢から選ばれた時(postback)に呼ばれるイベント    
    @LINE_HANDLER.add(PostbackEvent)
    def on_postback(line_event):
        data = line_event.postback.data
        LINE_BOT_API.reply_message(line_event.reply_token, TextSendMessage("{0}を選択しましたね!".format(data)))
    
    LINE_HANDLER.handle(body, signature)
    return 0

def make_select_message():
    return TemplateSendMessage(
        alt_text="選択肢",
        template=ButtonsTemplate(
            title="選択肢のテスト",
            text="下から1つ選んでね!",
            actions=[
                {
                    "type": "postback",
                    "data": "morning",
                    "label": ""
                },
                {
                    "type": "postback",
                    "data": "noon",
                    "label": ""
                },
                {
                    "type": "postback",
                    "data": "night",
                    "label": ""
                }
            ]
        )
    )

動作確認した結果は以下の通りです。
image.png

位置情報

位置情報を受け取ったり、応答するための実装です。
・「現在位置」を受け取り「その場所の緯度経度は ({緯度}, {経度}) ですね!」と応答する。
・「佐鳴湖」というメッセージを受け取ったら、佐鳴湖の場所を応答する。

lambda_function.py
import os
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,
    TemplateSendMessage, ButtonsTemplate,
    PostbackEvent,
    LocationMessage, LocationSendMessage
)
LINE_CHANNEL_ACCESS_TOKEN   = os.environ['LINE_CHANNEL_ACCESS_TOKEN']
LINE_CHANNEL_SECRET         = os.environ['LINE_CHANNEL_SECRET']
LINE_BOT_API = LineBotApi(LINE_CHANNEL_ACCESS_TOKEN)
LINE_HANDLER = WebhookHandler(LINE_CHANNEL_SECRET)

def lambda_handler(event, context):
    logger.info(event)
    signature = event["headers"]["x-line-signature"]
    body = event["body"]
    
    # テキストメッセージを受け取った時に呼ばれるイベント
    @LINE_HANDLER.add(MessageEvent, message=TextMessage)
    def on_message(line_event):
        # ユーザー情報を取得する
        profile = LINE_BOT_API.get_profile(line_event.source.user_id)
        logger.info(profile)

        message = line_event.message.text.lower()
        if message == '選択肢':
            LINE_BOT_API.reply_message(line_event.reply_token, make_select_message())
        elif message == '佐鳴湖':
            LINE_BOT_API.reply_message(line_event.reply_token, make_sanaruko_location_message())
        else:
            LINE_BOT_API.reply_message(line_event.reply_token, TextSendMessage("こんにちは!"))

    # 選択肢から選ばれた時(postback)に呼ばれるイベント    
    @LINE_HANDLER.add(PostbackEvent)
    def on_postback(line_event):
        data = line_event.postback.data
        LINE_BOT_API.reply_message(line_event.reply_token, TextSendMessage("{0}を選択しましたね!".format(data)))
        
    # 位置情報を受け取った時に呼ばれるイベント
    @LINE_HANDLER.add(MessageEvent, message=LocationMessage)
    def on_location(line_event):
        latlon = "({0}, {1})".format(line_event.message.latitude, line_event.message.longitude)
        LINE_BOT_API.reply_message(line_event.reply_token, TextSendMessage("その場所の緯度経度は {0} ですね!".format(latlon)))

    LINE_HANDLER.handle(body, signature)
    return 0

def make_select_message():
    return TemplateSendMessage(
        alt_text="選択肢",
        template=ButtonsTemplate(
            title="選択肢のテスト",
            text="下から1つ選んでね!",
            actions=[
                {
                    "type": "postback",
                    "data": "morning",
                    "label": ""
                },
                {
                    "type": "postback",
                    "data": "noon",
                    "label": ""
                },
                {
                    "type": "postback",
                    "data": "night",
                    "label": ""
                }
            ]
        )
    )
    
def make_sanaruko_location_message():
    title = '佐鳴湖'
    address = '〒432-8002 静岡県浜松市中区富塚町5195'
    lat = 34.707433242045255
    lng = 137.68702025092614
    return LocationSendMessage(title=title, address=address, latitude=lat, longitude=lng)

「位置情報」は、LINEのメッセージ入力欄の横にカメラや画像のアイコンがありますが、その横の「+」アイコンを選択すると表示されるメニューの中にあります。
image.png

動作確認した結果は以下の通りです。
image.png

あとがき

本当は、画像を受信して画像処理して返す、みたいなものも載せようと思ってたのですが辞めました。
バイナリデータのまま返せるかと思っていたらそうではなく、どこかに公開してそのURLを返してあげる必要があるようで、ちょっと長くなりそうだなと思ったためです。
この記事に載せるのは辞めましたが、いつかこれをテーマにした記事を書こうと思います。

1年前に、顔や文字を検出してモザイクをかけるサーバーレスWebアプリ「モザイク」を作りましたが、これのLINE BOT版を作ってみようかな~。って思ってます。

16
16
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?