はじめに
こちらの記事で紹介している自作Webアプリが今のままだと実用性がなさすぎるのでLINEbotにしてみました。
LINEbotを実装しようと思うとHerokuが定番?な感じですが去年AWSのソリューションアーキテクトアソシエイトを取得したこともあり、せっかくなのでAWS使おうと思いlambdaで実装。
以下参考文献になります。lambdaにデプロイするときとかの勉強させて頂きました。
メインのコード
import os
import sys
from linebot import (
LineBotApi, WebhookHandler
)
from linebot.exceptions import (
LineBotApiError, InvalidSignatureError
)
from linebot.models import (
MessageEvent, TextMessage, TextSendMessage
)
import urllib.parse
import json
import requests
import random
import logging
logger = logging.getLogger()
logger.setLevel(logging.ERROR)
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)
line_bot_api = LineBotApi(channel_access_token)
handler = WebhookHandler(channel_secret)
def lambda_handler(event, context):
if "x-line-signature" in event["headers"]:
signature = event["headers"]["x-line-signature"]
elif "X-Line-Signature" in event["headers"]:
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"}
def Budget_to_Code(budget):
budget = int(budget)
if budget <= 500:
code = "B009"
elif 500<budget<=1000:
code = "B010"
elif 1000<budget<=1500:
code = "B011"
elif 1500<budget<=2000:
code = "B001"
elif 2000<budget<=3000:
code = "B002"
elif 3000<budget<=4000:
code = "B003"
elif 4000<budget<=5000:
code = "B008"
elif 5000<budget<=7000:
code = "B004"
elif 7000<budget<=10000:
code = "B005"
elif 10000<budget<=15000:
code = "B006"
elif 15000<budget<=20000:
code = "B012"
elif 20000<budget<=30000:
code = "B013"
elif budget>=30000:
code = "B014"
return code
def RailAPI(station, pref):
station_url =urllib.parse.quote(station)
pref_url =urllib.parse.quote(pref)
api='http://express.heartrails.com/api/json?method=getStations&name={station_name}&prefecture={pref_name}'
url=api.format(station_name=station_url, pref_name=pref_url)
response=requests.get(url)
result_list = json.loads(response.text)['response']['station']
lng=result_list[0]['x']
lat=result_list[0]['y']
return lat, lng
def HotpepperAPI(lat, lng, free_drink=0, code=None):
api_key="" #APIキー
if code:
api = "http://webservice.recruit.co.jp/hotpepper/gourmet/v1/?" \
"key={key}&lat={lat}&lng={lng}&budget={code}&free_drink={free_drink}&range=3&count=200&order=1&format=json"
else:
api = "http://webservice.recruit.co.jp/hotpepper/gourmet/v1/?" \
"key={key}&lat={lat}&lng={lng}&free_drink={free_drink}&range=3&count=200&order=1&format=json"
url=api.format(key=api_key,lat=lat, lng=lng, code=code, free_drink=free_drink)
response = requests.get(url)
result_list = json.loads(response.text)['results']['shop']
shop_datas=[]
for shop_data in result_list:
shop_datas.append([shop_data["name"],shop_data["address"],shop_data["urls"]['pc'],shop_data["free_drink"], shop_data["budget"]['average'],shop_data["open"]])
shop_datas = random.sample(shop_datas, 3)
return shop_datas
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
#テキストメッセージを取得
text = event.message.text.split()
if len(text) == 4:
pref = text[0]
station = text[1]
budget = text[2]
drink = 1
#予算のコード変換
budget = Budget_to_Code(budget)
#apiで検索
lat, lng = RailAPI(station, pref)
shop_datas = HotpepperAPI(lat, lng, free_drink=drink, code=budget)
elif len(text) == 3:
pref = text[0]
station = text[1]
budget= text[2]
budget = Budget_to_Code(budget)
lat, lng = RailAPI(station, pref)
shop_datas = HotpepperAPI(lat, lng, code=budget)
elif len(text) == 2:
pref = text[0]
station = text[1]
lat, lng = RailAPI(station, pref)
shop_datas = HotpepperAPI(lat, lng)
else:
#形式に合っていない場合はエラー
with open("explanation.txt", "r") as f:
text = f.read()
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text=text)
)
return
shop1 = shop_datas[0]
shop2 = shop_datas[1]
shop3 = shop_datas[2]
#店舗情報を返信
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text=f"{shop1[0]}\n住所:{shop1[1]}\nURL:{shop1[2]}\n{shop2[0]}\n住所:{shop2[1]}\nURL:{shop2[2]}\n{shop3[0]}\n住所:{shop3[1]}\nURL:{shop3[2]}\n")
)
try:
handler.handle(body, signature)
except LineBotApiError as e:
logger.error("Got exception from LINE Messaging API: %s\n" % e.message)
for m in e.error.details:
logger.error(" %s: %s" % (m.property, m.message))
return error_json
except InvalidSignatureError:
return error_json
return ok_json
コードの中身はほぼ変えてません。flaskの要素をとっぱらってlambdaの関数の中に入れただけみたいな、、、
explanation.txtには形式外の入力に対する返答のコピペをいれてます。
LINEbotのシークレットキーとアクセスキーはAWSでlambdaに環境変数として渡してありますが、Hotpepperの方は変えてません(コードの変更めんどくさいから環境変数にした方がいい)
AWSの設定
構成としてはこれだけです。めっちゃ単純。
1.Lambda(test_function)を作成する
2.Lambdaにコードとコードに必要なライブラリが入ったzipファイルをアップロードする
3.トリガーにAPI Gatewayを作成
4.作成されたエンドポイントURLをMessaging APIのWebhook URLに登録
これだけです!
具体的なやり方は色々な方が記事にされてるので省略します。
実際に作ったLINEbot
入力の制限は多々ありますがとりあえず動いてよかったです。
感想
思ったより簡単に作れました。ただわかってはいましたが、AWSの資格をとるのと実際に実装するのは別物ですね、、、
もっと勉強したいと思います。
1つ疑問に思ったのが、今回API GatewayのAPIタイプはHTTPにしたのですが、この場合だとWebhookの特徴でもある双方向はできないってことかな?多分そうですよね。
アプリの仕様上HTTPでまったく問題ないので今回はこれでよかったですが、もっと理解を深めないとなと実感しました。
4月に応用情報技術者を受けるので一旦開発は休憩します。
落ち着いたら競馬予想AI作りたいなーーー