想定対象
- MySQL触れる人
- Flaskは初めて触る人
- LINE bot の環境を作ったが具体的なユーザーとのやりとりの仕方がわからない人
- LINE bot のエコーサーバー的なものから先に進まない人
- ドキュメントやライブラリのソースコードは
読めない読みたくない人
LINE bot のpython+flask+herokuでの環境構築は
FlaskでLINE botを実装し,herokuにdeployするまでを参照。
例えば
最近は仮想通貨のBotが流行っているみたいですが、一昔前のMT4のようにシグナルをスマホに配信したい場合にLINE botではユーザーIDが必要になります。
ユーザーIDを受け取る方法はいくつもありますが、ユーザーが友達追加したタイミングで取得しておくのが自然です。
取得したデータをどこに保存するかといえば、データベースです。herokuのDBでも良いけど、今回は汎用的に使えるように外部のデータベース使用を想定します
内容
準備
先にライブラリをインストールします。
>>> pip install flask
>>> pip install line-bot-sdk
>>> pip install MySQLdb || pip install pymysql
※MySQLdbは今回ダメだったのでpymysqlで代用(DBを使わない人は不要)
- ディレクトリ構造
>>> tree
.
├── runtime.txt
├── Procfile
├── requirements.txt
├── conf.json
└── app.py
- このファイルたちの中身
1.Pythonのバージョン
python-3.6.4
2.heroku上で実行されるコマンド
web: python main.py
3.Pythonライブラリ(pip freeze > requirements.txt
で出力していらないものを消す)
line-bot-sdk==1.5.0
future==0.16.0
requests==2.18.4
Flask==0.12.2
click==6.7
itsdangerous==0.24
Jinja2==2.10
MarkupSafe==1.0
Werkzeug==0.14.1
mysqlclient==1.3.12
PyMySQL==0.8.0
4.設定ファイル (環境変数を使うならいらない・データベース関係は使う人だけ)
{
"CHANNEL_SECRET": "LINE APIのチャネルシークレット",
"CHANNEL_ACCESS_TOKEN": "LINE APIのアクセストークン",
"REMOTE_HOST": "データベースのホストIPもしくはホスト名",
"REMOTE_DB_NAME": "データベースの名前",
"REMOTE_DB_USER": "データベースのユーザ名",
"REMOTE_DB_PASS": "データベースのパスワード",
"REMOTE_DB_TB": "データベースのテーブル名",
"COMMENT": "パーミッションは600とかにしましょう"
}
5.メインのファイル
# -*- coding: utf-8 -*-
import os
import sys
import json
from decimal import Decimal #金融系の計算で丸め誤差を排除するために必要なライブラリ
try:
import MySQLdb
except:
import pymysql
pymysql.install_as_MySQLdb()
import MySQLdb
from argparse import ArgumentParser
from flask import Flask, request, abort
from linebot import (
LineBotApi, WebhookHandler
)
from linebot.exceptions import (
InvalidSignatureError
)
from linebot.models import ( # 使用するモデル(イベント, メッセージ, アクションなど)を列挙
FollowEvent, UnfollowEvent, MessageEvent, PostbackEvent,
TextMessage, TextSendMessage, TemplateSendMessage,
ButtonsTemplate, CarouselTemplate, CarouselColumn,
PostbackTemplateAction
)
app = Flask(__name__)
ABS_PATH = os.path.dirname(os.path.abspath(sys.argv[0]))
with open(ABS_PATH+'/conf.json', 'r') as f:
CONF_DATA = json.load(f)
CHANNEL_SECRET = CONF_DATA['CHANNEL_SECRET']
CHANNEL_ACCESS_TOKEN = CONF_DATA['CHANNEL_ACCESS_TOKEN']
REMOTE_HOST = CONF_DATA['REMOTE_HOST']
REMOTE_DB_NAME = CONF_DATA['REMOTE_DB_NAME']
REMOTE_DB_USER = CONF_DATA['REMOTE_DB_USER']
REMOTE_DB_PASS = CONF_DATA['REMOTE_DB_PASS']
REMOTE_DB_TB = CONF_DATA['REMOTE_DB_TB']
if CHANNEL_SECRET is None:
print('Specify LINE_CHANNEL_SECRET.')
sys.exit(1)
if CHANNEL_ACCESS_TOKEN is None:
print('Specify LINE_CHANNEL_ACCESS_TOKEN.')
sys.exit(1)
line_bot_api = LineBotApi(CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(CHANNEL_SECRET)
# https://アプリ名.herokuapp.com/test にアクセスしてtest okが表示されればデプロイ自体は成功してる
# flaskは@app.route("/ディレクトリ名")でルーティングする
@app.route("/test")
def test():
return('test ok')
# LINE APIにアプリがあることを知らせるためのもの
@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'
# メッセージが来た時の反応
@handler.add(MessageEvent, message=TextMessage)
def message_text(event):
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text=msg)
)
この状態でLINE botにメッセージを送信すれば、送信した内容をエコーして返してくれます。
イベントの受け取り
イベントにはFollowEvent、UnfollowEventなどがあります。
今回は「FollowEvent(友達追加・ブロック解除時に起動)を検知」
→「ユーザー情報のDB保存」「メッセージ送信」をします
# Follow Event
@handler.add(FollowEvent)
def on_follow(event):
reply_token = event.reply_token
user_id = event.source.user_id
profiles = line_bot_api.get_profile(user_id=user_id)
display_name = profiles.display_name
picture_url = profiles.picture_url
status_message = profiles.status_message
# DBへの保存
try:
conn = MySQLdb.connect(user=REMOTE_DB_USER, passwd=REMOTE_DB_PASS, host=REMOTE_HOST, db=REMOTE_DB_NAME)
c = conn.cursor()
sql = "SELECT `id` FROM`"+REMOTE_DB_TB+"` WHERE `user_id` = '"+user_id+"';"
c.execute(sql)
ret = c.fetchall()
if len(ret) == 0:
sql = "INSERT INTO `"+REMOTE_DB_TB+"` (`user_id`, `display_name`, `picture_url`, `status_message`, `status`)\
VALUES ('"+user_id+"', '"+str(display_name)+"', '"+str(picture_url)+"', '"+str(status_message)+"', 1);"
elif len(ret) == 1:
sql = "UPDATE `"+REMOTE_DB_TB+"` SET `display_name` = '"+str(display_name)+"', `picture_url` = '"+str(picture_url)+"',\
`status_message` = '"+str(status_message)+"', `status` = '1' WHERE `user_id` = '"+user_id+"';"
c.execute(sql)
conn.commit()
finally:
conn.close()
c.close()
# メッセージの送信
line_bot_api.reply_message(
reply_token=reply_token,
messages=TextSendMessage(text='メッセージArigato!\nです')
)
※他のイベントを受け取りたい場合は@handler.add(他のイベント名)
にすればokです
プッシュ通知
DBにユーザーIDを保存したことで、これを使ってプッシュ通知ができます。
プッシュ通知はイベントをトリガーとしないので@handler.add
は不要です
任意のタイミングで呼び出して下さい
def send_push_message(user_id=None, content=None):
if user_id is None or content is None:
return False
line_bot_api.push_message(
to=user_id,
messages=TextSendMessage(text='メッセージがPushされたよ!')
)
※実際の開発ではPush通知自体よりもuser_idを取得するところでつまづきがちです。
ユーザーからの入力を受け取る(PostbackAction・ButtonsTemplate)
LINE APIでのユーザーからの入力は基本的にメッセージ送信です
しかし、メッセージ形式だと自由度が高すぎてユーザーは何を送信して良いのか分かりません。
そこでLINE APIではTemplate(テンプレート)が用意されています。
今回は「ON/OFF」が押せるボタンを作成します
# ボタンの入力を受け取るPostbackEvent
@handler.add(PostbackEvent)
def on_postback(event):
reply_token = event.reply_token
user_id = event.source.user_id
postback_msg = event.postback.data
if postback_msg == 'is_show=1':
line_bot_api.push_message(
to=user_id,
messages=TextSendMessage(text='is_showオプションは1だよ!')
)
elif postback_msg == 'is_show=0':
line_bot_api.push_message(
to=user_id,
messages=TextSendMessage(text='is_showオプションは0だよ!')
)
# ボタンを送信する
def send_button(event, user_id):
message_template = ButtonsTemplate(
text='BTC_JPYの通知',
actions=[
PostbackTemplateAction(
label='ON',
data='is_show=1'
),
PostbackTemplateAction(
label='OFF',
data='is_show=0'
)
]
)
line_bot_api.push_message(
to=user_id,
messages=TemplateSendMessage(
alt_text='button template',
template=message_template
)
)
セレクトボックスやチェックボックスの入力受け取りは今のところ未対応な気がします
カスタマイズ可能な要素はTemplateやRichmenuで、ということのようです
Carousel(カルーセル)の使い方
ここまでが出来ていれば他のメソッドも簡単に実装できると思いますがCarouselが少し厄介だったので紹介します。
carouselとは横にスクロルできるやつです
def show_carousel(user_id):
carousel_columns = [
CarouselColumn(
text=value,
title=value+'の通知',
actions=[
PostbackTemplateAction(
label='ON',
data=value+'1'
),
PostbackTemplateAction(
label='OFF',
data=value+'0'
)
]
) for key, value in (
zip(
('取引所', '取引所', '取引所', '取引所', '取引所'),
('Binance', 'KuCoin', 'Hupbipro', 'Poloniex', 'Bittrex')
)
)
]
message_template = CarouselTemplate(columns=carousel_columns)
line_bot_api.push_message(
to=user_id,
messages=TemplateSendMessage(alt_text='carousel template', template=message_template)
)
内容をうまくいじればこんな感じに出力できますヽ(*・ω・)ノ
あとがき(言い訳)
業務に使ったものを切り貼りしてるのでおかしな部分があるかもしれません。。(´・ω・`)