LoginSignup
12
7

More than 1 year has passed since last update.

【AWS】PythonによるLINE Bot構築

Last updated at Posted at 2022-10-31

【AWS】PythonによるLINE Bot構築

 LINEは日本人口の70~80%が利用しており、生活に必要不可欠な通信インフラと言っても差し支えないほど、若い世代から高齢の世代まで日常的に利用している。その中でサービスを展開している企業やお店が、LINE公式アカウントを通じて、顧客とコミュニケーションを可能にするサービスがMessaging APIである。今回は、LINE Messaging APIを用いて、LINE公式アカウントとAWS上に作成したWebアプリと連携することで、LINE Botを作成する方法を紹介する。

1. LINE Developersにログインして、Messaging API用のチャネルを作成する

下記LINE Developersのサイトのコンソールにアクセスして、自身のLINEアカウントなどでログインする。

次に、コンソール画面からLINEアプリ作成のため、Messaging API用のチャネルを作成する。

  1. Create a new Channelを押す
  2. Messaging APIを選択する
  3. Company or owner's country or regionでJapanを選択する
  4. Channel nameを決め、Channel descriptionに適当に書き入れる
  5. Category、Subcategoryで任意のものを選択して、Createを押す

作成したチャネルから、下記のAPI Keyを生成して、後程使うため控えておく。

  • Channel secret
  • Channel access token
LINEdevelopers1.PNG LINEdevelopers12.PNG

2. AWS Lambda関数作成

下記の手順に従ってAWS Lambda関数の作成を行う。

  1. AWSマネジメントコンソールからLambdaを選択する
  2. 関数の作成を押す
  3. 一から作成を選択する
  4. 関数名を適当な名前にして、ランタイムにPython 3.9を選択する
  5. 実行ロールを適切なものを選択し、関数を作成を押す
lambda1.PNG

3. AWS API Gatewayでトリガーとエンドポイント作成

下記の手順に従ってAWS API Gatewayの作成を行う。

  1. 先ほど作成したLambdaの関数のページへ行く
  2. 関数の概要の中のトリガーを追加を押す
  3. トリガーを選択ではAPI GateWay、Create a new APIを選択し、APIタイプは「HTTP」を選択する
  4. セキュリティはOpenを選択する
  5. 追加ボタンを押す
  6. 設定タブのトリガーの中にあるAPI GateWayのAPIエンドポイントをコピーしておく
APIgateway.PNG

4. Line DevelopersでMessaging APIにWebhook URLを登録する

  1. Messageing API設定の「Webhook URL」に先ほどコピーしたAPI GateWayのAPIエンドポイントを追記する
  2. verifyを押して「Success」と表示されることを確認する
Linedevelop3.PNG

5. Lambdaの関数の環境設定をする

  1. Lambdaの関数のページへ行く
  2. 設定->環境変数で先ほどLINE Developersで取得したAPI Keyを登録する
LINE_CHANNEL_ACCESS_TOKEN = ****
LINE_CHANNEL_SECRET = ****
lambda3.PNG

6. LINE botのPythonアプリを作成する

下記のPythonコードを先ほど作成したLambdaの関数のランタイム設定の******.lambda_handlerの名前で保存する。※デフォルトではlambda_functionとなっているはず。

次にPythonコードを保存したフォルダで必要なPython moduleをInstallする。Install後、PythonライブラリファイルとPythonコードをzip形式で適当なファイル名で圧縮する。

pip install -t . linebot
lambda_function.py
import os
import sys
import logging
import requests
import urllib
import urllib.request
import json

from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.models import (MessageEvent, TextMessage, TextSendMessage)
from linebot.models import RichMenu, RichMenuArea, RichMenuBounds, RichMenuSize,URIAction
from linebot.models import CameraAction, CameraRollAction
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,
    SourceUser, SourceGroup, SourceRoom,
    TemplateSendMessage, ConfirmTemplate, MessageTemplateAction,
    ButtonsTemplate, ImageCarouselTemplate, ImageCarouselColumn, URITemplateAction,
    PostbackTemplateAction, DatetimePickerTemplateAction,
    CarouselTemplate, CarouselColumn, PostbackEvent,
    StickerMessage, StickerSendMessage, LocationMessage, LocationSendMessage, ImageSendMessage, VideoSendMessage,
    ImageMessage, VideoMessage, AudioMessage, FileMessage,
    UnfollowEvent, FollowEvent, JoinEvent, LeaveEvent, BeaconEvent, FlexSendMessage
)
from linebot.models.actions import PostbackAction

from linebot.exceptions import (LineBotApiError, InvalidSignatureError)

logger = logging.getLogger()
logger.setLevel(logging.ERROR)

#LINEBOTと接続するための記述
#環境変数からLINEBotのチャンネルアクセストークンとシークレットを読み込む
channel_secret = os.getenv('LINE_CHANNEL_SECRET', None)
channel_access_token = os.getenv('LINE_CHANNEL_ACCESS_TOKEN', None)

#無いならエラー
if channel_secret is None:
    logger.error('Specify LINE_CHANNEL_SECRET as environment variable.')
    sys.exit(1)
if channel_access_token is None:
    logger.error('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.')
    sys.exit(1)

#apiとhandlerの生成(チャンネルアクセストークンとシークレットを渡す)
line_bot_api = LineBotApi(channel_access_token)
handler = WebhookHandler(channel_secret)

#Lambdaのメインの動作
def lambda_handler(event, context):

#認証用のx-line-signatureヘッダー
    signature = event["headers"]["x-line-signature"]
    body = event["body"]

#リターン値の設定
    ok_json = {"isBase64Encoded": False,
               "statusCode": 200,
               "headers": {},
               "body": ""}
    error_json = {"isBase64Encoded": False,
                  "statusCode": 500,
                  "headers": {},
                  "body": "Error"}


    #以下でWebhookから送られてきたイベントをどのように処理するかを記述する
    @handler.add(MessageEvent, message=TextMessage)
    def handle_message(event):
        send_message = event.message.text
        rep = talkapi(send_message)
        display_name = 'None'
        if isinstance(event.source, SourceUser):
            profile = line_bot_api.get_profile(event.source.user_id)
            user_id = event.source.user_id
            display_name = profile.display_name
        else: print("user profile can't not use")
        if send_message == "今日のドリンクメニュー" and isinstance(event.source, SourceUser):
            profile = line_bot_api.get_profile(event.source.user_id)
            tmpname = profile.display_name
            line_bot_api.reply_message(
                event.reply_token,
                (ImageSendMessage(original_content_url="https://maindepository.s3.ap-northeast-1.amazonaws.com/cafe_menu.png",
                                  preview_image_url="https://maindepository.s3.ap-northeast-1.amazonaws.com/cafe_menu.png")))
        elif send_message == "お問い合わせ" and isinstance(event.source, SourceUser):
            bubble_string = """
                {
                  "type": "bubble",
                  "header": {
                    "type": "box",
                    "layout": "vertical",
                    "contents": [
                      {
                        "type": "text",
                        "text": "お問い合わせ",
                        "weight": "bold",
                        "align": "center",
                        "color": "#ffffff"
                      },
                      {
                        "type": "text",
                        "text": "お困りの状況に該当するものをお選びください。",
                        "wrap": true,
                        "color": "#ffffff"
                      }
                    ],
                    "backgroundColor": "#00CC62"
                  },
                  "body": {
                    "type": "box",
                    "layout": "vertical",
                    "contents": [
                      {
                        "type": "box",
                        "layout": "vertical",
                        "contents": [
                          {
                            "type": "text",
                            "text": "何時から開いていますか?",
                            "align": "center",
                            "color": "#42659a"
                          }
                        ],
                        "action": {
                          "type": "postback",
                          "label": "question",
                          "data": "action=question&id=1",
                          "displayText": "何時から開いていますか?"
                        }
                      },
                      {
                        "type": "box",
                        "layout": "vertical",
                        "contents": [
                          {
                            "type": "text",
                            "text": "店舗はどこにありますか?",
                            "color": "#42659a",
                            "align": "center"
                          }
                        ],
                        "margin": "12px",
                        "action": {
                          "type": "postback",
                          "label": "question",
                          "data": "action=question&id=2",
                          "displayText": "店舗はどこにありますか?"
                        }
                      },
                      {
                        "type": "box",
                        "layout": "vertical",
                        "contents": [
                          {
                            "type": "text",
                            "text": "駐車場はありますか?",
                            "color": "#42659a",
                            "align": "center"
                          }
                        ],
                        "margin": "12px",
                        "action": {
                          "type": "postback",
                          "label": "question",
                          "data": "action=question&id=3",
                          "displayText": "駐車場はありますか?"
                        }
                      },
                      {
                        "type": "box",
                        "layout": "vertical",
                        "contents": [
                          {
                            "type": "text",
                            "text": "テイクアウトやデリバリーはありますか?",
                            "align": "center",
                            "color": "#42659a"
                          }
                        ],
                        "margin": "12px",
                        "action": {
                          "type": "postback",
                          "label": "question",
                          "data": "action=question&id=4",
                          "displayText": "テイクアウトやデリバリーはありますか?"
                        }
                      }
                    ]
                  }
                }
            """
            message = FlexSendMessage(alt_text="お問い合わせ", contents=json.loads(bubble_string))
            line_bot_api.reply_message(
                event.reply_token,
                message
            )
    
    @handler.add(PostbackEvent)
    def handle_postback(event):
        if event.postback.data == 'action=question&id=1':
            line_bot_api.reply_message(
                event.reply_token,
                (TextSendMessage(text='平日は8:30~17:00、祝休日は8:30~21:00まで開いています。'))
            )
        elif event.postback.data == 'action=question&id=2':
            line_bot_api.reply_message(
                event.reply_token,
                (TextSendMessage(text='下記リンクからGoogle Mapで確認できます。\nhttps://goo.gl/maps/m2KbyY6RA8QepLaa8'))
            )
        elif event.postback.data == 'action=question&id=3':
            line_bot_api.reply_message(
                event.reply_token,
                (TextSendMessage(text='はい、車6台が停めれる駐車場がございます。また、駐輪場もございます。'))
            )
        elif event.postback.data == 'action=question&id=4':
            line_bot_api.reply_message(
                event.reply_token,
                (TextSendMessage(text='申し訳ございません。デリバリーもテイクアウトも今はやっておりません'))
            )        

    @handler.add(FollowEvent)
    def handle_follow(event):
        line_bot_api.reply_message(
            event.reply_token,
            TextSendMessage(text='登録して頂きありがとうございます!Cafe BORCELLEのLINE公式アカウントです。\n\n下記Menuから今日のドリンクメニューやクーポン、お問い合わせ、ホームページのリンクがご確認頂けます。\n\n自動会話botと連携しており、チャットで話しかけると返答が返ってきます。\n\nぜひご活用頂ければ幸いです。'))
    
#例外処理としての動作
    try:
        handler.handle(body, signature)
    except LineBotApiError as e:
        logger.error("Got exception from LINE Messaging API: %s\n" % e.handle_message)
        for m in e.error.details:
            logger.error("  %s: %s" % (m.property, m.handle_message))
        return error_json
    except InvalidSignatureError:
        return error_json

    return ok_json

7. LINE botのPythonコードをLambdaへアップロードする

  1. 先ほど作成したLambdaの関数のページへ行く
  2. 右側のアップロード元をクリックする
  3. .zipファイルを選択する
  4. 先ほど作成したPythonコードを選択する
  5. 保存を押す

7. 動作確認する

LINE developersのWebhookl URLのverifyを押して、successが出るか試してみる。もしエラーが起きたら、正常にHTTP通信ができていないので、Lambdaの関数ページのモニタリング->ログでエラーを確認する。

まとめ

Herokuが2022/11/28~有料になったため、別のPaaSへの移管として、今回AWSでLINE Botを作ってみた。公開アプリに関してはAWS LambdaとAPI Gatewayで簡単に作成できた。認証を加えたい場合などはIAMなどで、認証キーの作成などを行う必要があると思う。

12
7
0

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
12
7