【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用のチャネルを作成する。
- Create a new Channelを押す
- Messaging APIを選択する
- Company or owner's country or regionでJapanを選択する
- Channel nameを決め、Channel descriptionに適当に書き入れる
- Category、Subcategoryで任意のものを選択して、Createを押す
作成したチャネルから、下記のAPI Keyを生成して、後程使うため控えておく。
- Channel secret
- Channel access token
2. AWS Lambda関数作成
下記の手順に従ってAWS Lambda関数の作成を行う。
- AWSマネジメントコンソールからLambdaを選択する
- 関数の作成を押す
- 一から作成を選択する
- 関数名を適当な名前にして、ランタイムにPython 3.9を選択する
- 実行ロールを適切なものを選択し、関数を作成を押す
3. AWS API Gatewayでトリガーとエンドポイント作成
下記の手順に従ってAWS API Gatewayの作成を行う。
- 先ほど作成したLambdaの関数のページへ行く
- 関数の概要の中のトリガーを追加を押す
- トリガーを選択ではAPI GateWay、Create a new APIを選択し、APIタイプは「HTTP」を選択する
- セキュリティはOpenを選択する
- 追加ボタンを押す
- 設定タブのトリガーの中にあるAPI GateWayのAPIエンドポイントをコピーしておく
4. Line DevelopersでMessaging APIにWebhook URLを登録する
- Messageing API設定の「Webhook URL」に先ほどコピーしたAPI GateWayのAPIエンドポイントを追記する
- verifyを押して「Success」と表示されることを確認する
5. Lambdaの関数の環境設定をする
- Lambdaの関数のページへ行く
- 設定->環境変数で先ほどLINE Developersで取得したAPI Keyを登録する
LINE_CHANNEL_ACCESS_TOKEN = ****
LINE_CHANNEL_SECRET = ****
6. LINE botのPythonアプリを作成する
下記のPythonコードを先ほど作成したLambdaの関数のランタイム設定の******.lambda_handlerの名前で保存する。※デフォルトではlambda_functionとなっているはず。
次にPythonコードを保存したフォルダで必要なPython moduleをInstallする。Install後、PythonライブラリファイルとPythonコードをzip形式で適当なファイル名で圧縮する。
pip install -t . linebot
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へアップロードする
- 先ほど作成したLambdaの関数のページへ行く
- 右側のアップロード元をクリックする
- .zipファイルを選択する
- 先ほど作成したPythonコードを選択する
- 保存を押す
7. 動作確認する
LINE developersのWebhookl URLのverifyを押して、successが出るか試してみる。もしエラーが起きたら、正常にHTTP通信ができていないので、Lambdaの関数ページのモニタリング->ログでエラーを確認する。
まとめ
Herokuが2022/11/28~有料になったため、別のPaaSへの移管として、今回AWSでLINE Botを作ってみた。公開アプリに関してはAWS LambdaとAPI Gatewayで簡単に作成できた。認証を加えたい場合などはIAMなどで、認証キーの作成などを行う必要があると思う。