0
2

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 1 year has passed since last update.

作ったWebアプリをLINEbotにしてAWS lambdaで実装

Last updated at Posted at 2023-03-15

はじめに

こちらの記事で紹介している自作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の設定

image.png

構成としてはこれだけです。めっちゃ単純。
1.Lambda(test_function)を作成する
2.Lambdaにコードとコードに必要なライブラリが入ったzipファイルをアップロードする
3.トリガーにAPI Gatewayを作成
4.作成されたエンドポイントURLをMessaging APIのWebhook URLに登録

これだけです!

具体的なやり方は色々な方が記事にされてるので省略します。

実際に作ったLINEbot

image.png

形式外の入力時
image.png

入力の制限は多々ありますがとりあえず動いてよかったです。

感想

思ったより簡単に作れました。ただわかってはいましたが、AWSの資格をとるのと実際に実装するのは別物ですね、、、
もっと勉強したいと思います。

1つ疑問に思ったのが、今回API GatewayのAPIタイプはHTTPにしたのですが、この場合だとWebhookの特徴でもある双方向はできないってことかな?多分そうですよね。
アプリの仕様上HTTPでまったく問題ないので今回はこれでよかったですが、もっと理解を深めないとなと実感しました。

4月に応用情報技術者を受けるので一旦開発は休憩します。

落ち着いたら競馬予想AI作りたいなーーー

0
2
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
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?