見出し画像

python:LINEの位置情報で近くのカレー屋さんを通知する

今回は、LINE Messenger APIを使って、位置情報を入力することで近くのカレー屋さんを連絡してくれるプログラムを作ってみました。


LINEチャンネル『最寄りカレー』

画像3


参考にしたサイト


おおまかな流れ

①LINEで位置情報を受け取る

②位置情報をもとにぐるなびAPIを使って、近くのカレー屋を抽出

③カレー屋の情報を通知


完成コード

app.py

import sys
import json
import os
from flask import Flask, request, abort, send_file
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import (
   CarouselColumn, CarouselTemplate, FollowEvent,
   LocationMessage, MessageEvent, TemplateSendMessage,
   TextMessage, TextSendMessage, UnfollowEvent, URITemplateAction
)
import requests
import urllib.parse


app = Flask(__name__)

 #環境変数の設定 
channel_secret = os.environ['channel_secret']
channel_access_token = os.environ['channel_access_token']
gurunavi_api = os.environ['gurunavi_api']

 #LINE  APIの設定
line_bot_api = LineBotApi(channel_access_token)
handler = WebhookHandler(channel_secret) #その他 
no_hit_message = "近くにカレー屋さんはないようです"
DAMMY_URL = "https://canbus.com/blog/wp-content/uploads/2018/02/2015-tenpo.jpg"

 #テスト用 
@app.route("/")
def hello_world():
  return "hello world!"

 #関数の呼び出し処理 
@app.route("/callback", methods=['POST'])
def callback():
   signature = request.headers['X-Line-Signature']
   body = request.get_data(as_text=True)
   app.logger.info("Request body: " + body)
   try:
       handler.handle(body, signature)
   except InvalidSignatureError:
       abort(400)
   return 'OK'

 #ぐるなび検索 
def search_rest(lat, lon):
   url = "https://api.gnavi.co.jp/RestSearchAPI/v3/"
   params = {}
   params['latitude'] = lat
   params['longitude'] = lon
   params['keyid'] = gurunavi_api
   params['range'] = 3
   params['freeword'] = "カレー"
   response = requests.get(url, params)
   results = response.json()
   if "error" in results:
       if "message" in results:
           raise Exception("{}".format(results["message"]))
       else:
           raise Exception(DEF_ERR_MESSAGE)
   total_hit_count = results.get("total_hit_count", 0)
   if total_hit_count < 1:
       raise Exception(no_hit_message)
   return results

 #メイン処理 
@handler.add(MessageEvent, message=LocationMessage)
def handle_location_message(event):
   list = []
   user_lat = event.message.latitude
   user_longit = event.message.longitude
   rest_result = search_rest(user_lat, user_longit)
   for rest in rest_result.get("rest"):
       image_url = rest.get("image_url", {})
       image1 = image_url.get("shop_image1", "thumbnail_template.jpg")
       if image1 == "":
           image1 = DAMMY_URL
       name = rest.get("name", "")
       url = rest.get("url", "")
       pr = rest.get("pr", "")
       pr_short = "以下、内容\n" + pr.get("pr_short", "")
       if len(pr_short) >= 60:
           pr_short = pr_short[:56] + "…"
   
   
       result_dict = {
               "thumbnail_image_url": image1,
               "title": name,
               "text": pr_short,
               "actions": {
                   "label": "ぐるなびで見る",
                   "uri": url
               }
           }
       list.append(result_dict)
   print(list)
   columns = [
       CarouselColumn(
           thumbnail_image_url=column["thumbnail_image_url"],
           title=column["title"],
           text=column["text"],
           actions=[
               URITemplateAction(
                   label=column["actions"]["label"],
                   uri=column["actions"]["uri"],
               )
           ]
       )
       for column in list
   ]

   messages = TemplateSendMessage(
       alt_text="お近くのカレー屋さんについて連絡しました。",
       template=CarouselTemplate(columns=columns),
   )
   line_bot_api.reply_message(event.reply_token, messages=messages)


if __name__ == "__main__":
   port = int(os.getenv("PORT", 5000))
   app.run(host="0.0.0.0", port=port)


API関連初期準備

LINE Messenger APIとぐるなびAPIの登録が事前に必要です。



前処理と実行処理

import sys
import json
import os
from flask import Flask, request, abort, send_file
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import (
   CarouselColumn, CarouselTemplate, FollowEvent,
   LocationMessage, MessageEvent, TemplateSendMessage,
   TextMessage, TextSendMessage, UnfollowEvent, URITemplateAction
)
import requests
import urllib.parse

app = Flask(__name__)
 #環境変数の設定 
channel_secret = os.environ['channel_secret']
channel_access_token = os.environ['channel_access_token']
gurunavi_api = os.environ['gurunavi_api']
 #LINE  APIの設定
line_bot_api = LineBotApi(channel_access_token)
handler = WebhookHandler(channel_secret)
 #その他 
no_hit_message = "近くにカレー屋さんはないようです"
DAMMY_URL = "https://canbus.com/blog/wp-content/uploads/2018/02/2015-tenpo.jpg"
 #テスト用 
@app.route("/")
def hello_world():
  return "hello world!"
 #関数の呼び出し処理 
@app.route("/callback", methods=['POST'])
def callback():
   signature = request.headers['X-Line-Signature']
   body = request.get_data(as_text=True)
   app.logger.info("Request body: " + body)
   try:
       handler.handle(body, signature)
   except InvalidSignatureError:
       abort(400)
   return 'OK'

上記の部分は、いつも通りですね。

no_hit_messageは、ぐるなびで検索の結果が出ない場合に表示されるメッセージ。

DAMMY_URLは、店舗のイメージ画像がない場合に設定される画像を適当に入れています。


ぐるなび検索

def search_rest(lat, lon):
   url = "https://api.gnavi.co.jp/RestSearchAPI/v3/"
   params = {}
   params['latitude'] = lat
   params['longitude'] = lon
   params['keyid'] = gurunavi_api
   params['range'] = 3
   params['freeword'] = "カレー"
   response = requests.get(url, params)
   results = response.json()
   if "error" in results:
       if "message" in results:
           raise Exception("{}".format(results["message"]))
       else:
           raise Exception(DEF_ERR_MESSAGE)
   total_hit_count = results.get("total_hit_count", 0)
   if total_hit_count < 1:
       raise Exception(no_hit_message)
   return results

上記では、ぐるなびAPIを使って、位置情報の半径2,000m以内のキーワード『カレー』を含む店舗のjsonデータを取得しています。

各種パラメータについては、下記のURLに記載されています。


メイン処理


@handler.add(MessageEvent, message=LocationMessage)
def handle_location_message(event):
   list = []

   user_lat = event.message.latitude
   user_longit = event.message.longitude
   rest_result = search_rest(user_lat, user_longit)

   for rest in rest_result.get("rest"):
       image_url = rest.get("image_url", {})
       image1 = image_url.get("shop_image1", "thumbnail_template.jpg")
       if image1 == "":
           image1 = DAMMY_URL
       name = rest.get("name", "")
       url = rest.get("url", "")
       pr = rest.get("pr", "")
       pr_short = "以下、内容\n" + pr.get("pr_short", "")
       if len(pr_short) >= 60:
           pr_short = pr_short[:56] + "…"
   
   
       result_dict = {
               "thumbnail_image_url": image1,
               "title": name,
               "text": pr_short,
               "actions": {
                   "label": "ぐるなびで見る",
                   "uri": url
               }
           }

       list.append(result_dict)

   print(list)

上記では、以下のような流れでコードを作っています。

①空のリストを作成

②LINEからの位置情報で緯度と経度を受け取る

user_lat = event.message.latitude
   user_longit = event.message.longitude

③②の情報から、ぐるなび検索関数を実行

④リクエストパラメータのrestに絞って、for文で各情報を抽出

.get関数は、.get(辞書のキー, キーがない場合の値)
pr_shortでは、店舗の内容を入れ込んでいますが、空の場合と60文字以上の場合は、エラーが出るので、if関数で対応

⑤辞書型にして、リストに格納(のちに使用するLINEカルーセルのため)


LINEカルーセル

columns = [
       CarouselColumn(
           thumbnail_image_url=column["thumbnail_image_url"],
           title=column["title"],
           text=column["text"],
           actions=[
               URITemplateAction(
                   label=column["actions"]["label"],
                   uri=column["actions"]["uri"],
               )
           ]
       )
       for column in list
   ]

   messages = TemplateSendMessage(
       alt_text="お近くのカレー屋さんについて連絡しました。",
       template=CarouselTemplate(columns=columns),
   )

LINEカルーセルは、横にスライドできる画像付きのボックスみたいなものです。

キャプチャ


LINEカルーセルのリファレンスに従って、各項目を先ほど作った辞書を使って、入力していく。

キャプチャ



終わりに

フリーワードをカレーとしていますが、実際のところ、カレーが店舗情報にさえあれば、カレー屋以外の店舗も出てきてしまうので、そこは改善の余地があるかもしれません。


過去記事





この記事が気に入ったらサポートをしてみませんか?