目的
Pythonを用いて、LINE botを作成する
イメージは、「~~公式LINEアカウント」みたいな感じ
前提
- Python環境をインストール済み(今回はv3.6.5)
- LINE Developersに登録済み
- Herokuに登録済み
- GitHubに登録済み
参考
- LINE API
- LINE SDK(Python)
- PythonとLINE APIとHerokuで自動返信BOTを作る【Python編】
- LINEのBot開発 超入門(前編) ゼロから応答ができるまで
- LINE Messaging API を使ってLINEにメッセージ送信/メッセージ返信する
- HerokuとGitHubの連携
- 【LINE Notify】 PythonでLINEの メッセージと画像を送信する方法
- 【初心者向け】Gitってなに?①まず流れを理解する(コードなし)
- 【Python初心者! -LINE Botでオウム返し編-】
流れ
- LINE Developersに登録して、トークンなどを取得
- トークン、LINE SDKを用いて応答する仕組みを作成
- 管理はGitHubで、サーバはHeroku
- デプロイ後、動作確認
- 最終的なコードはこちら
コード書いてみた(修正前)
今回使用するコードです。クリックで開きます。
(ページ後半で修正済みコードもあります。)
main.py
...メッセージが入力されたら返信する。
account_response.py
...入力された内容によって、返信メッセージを選択する。
**main.py**
# -*- coding: utf-8 -*-
import os
import sys
from account_response import Response
from flask import Flask, request, abort
from linebot import (
LineBotApi, WebhookHandler
)
from linebot.exceptions import (
InvalidSignatureError
)
from linebot.models import (
MessageEvent, TextMessage, TextSendMessage,
)
app = Flask(__name__)
# Herokuの変数からトークンなどを取得
channel_secret = os.environ['LINE_CHANNEL_SECRET']
channel_access_token = os.environ['LINE_CHANNEL_ACCESS_TOKEN']
if channel_secret is None:
print('Specify LINE_CHANNEL_SECRET as environment variable.')
sys.exit(1)
if channel_access_token is None:
print('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.')
sys.exit(1)
line_bot_api = LineBotApi(channel_access_token)
handler = WebhookHandler(channel_secret)
# LINEからのWebhook
@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)
# 署名を検証し、問題なければhandleに定義されている関数を呼び出す。
try:
handler.handle(body, signature)
except InvalidSignatureError:
abort(400)
return 'OK'
#LINEでMessageEvent(普通のメッセージを送信された場合)が起こった場合
# reply_messageの第一引数のevent.reply_tokenは、イベントの応答に用いるトークンです。
# 第二引数には、linebot.modelsに定義されている返信用のTextSendMessageオブジェクトを渡しています。
@handler.add(MessageEvent, message = TextMessage)
def handle_message(event):
#入力された内容(event.message.text)に応じて返信する
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text = os.environ[Response.getResponse(event.message.text)])
)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=int(os.getenv("PORT", 5000))
**account_response.py**
# -*- coding: utf-8 -*-
class Response:
"""
dic: Herokuに登録してある変数
key以外の入力があったら、count+=1する
(Heroku上で1~4までのkeyに対応する変数を登録済み)
"""
dic={"会社概要":"COMPANY",
"事業内容":"SERVICE",
"採用情報":"CAREER",
"コマンド":"COMMAND",
"おはよう":"5",
"こんにちは":"5",
"こんばんは":"5",
"こんにちわ":"6",
"こんばんわ":"6"
}
count = 0
def getResponse(self,text):
if self.count >= 4:
self.count = 0
self.count += 1
for _dic in self.dic:
if _dic == text:
return self.dic[text]
return self.count
とりあえずコードはかけたので、デプロイしてみよう。
GitHubからHerokuへデプロイする
Herokuでアプリ作成後、DeployタブからGitHubを接続させる。
他の記事だとほとんどHeroku Git(Heroku CLI)を使っているが、今回はGitHubを使う。
自動デプロイを有効にしておく。
接続は無事に出来たので、実際にGitHubにアップロードしてデプロイしてみよう。
適当なファイルをアップロードしてコミットする。
Herokuでちゃんとデプロイできてるかな。
! No default language could be detected for this app.
HINT: This occurs when Heroku cannot detect the buildpack to use for this application automatically.
See https://devcenter.heroku.com/articles/buildpacks
! Push failed
ばっちりエラー出てますね、、、調べると以下が原因だった。
原因 |
---|
Buildpackの未設定 |
対策 |
---|
Herokuコンソール>Settings>Buildpacks 「Add Buildpack」で「Python」を選択、追加した |
どの言語をデプロイするか指定しないといけなかったのか。 |
では改めてアップロードする。 |
-----> App not compatible with buildpack: https://buildpack-registry.s3.amazonaws.com/buildpacks/heroku/python.tgz
More info: https://devcenter.heroku.com/articles/buildpacks#detection-failure
! Push failed
また安定のエラーがでましたね。
いろいろ調べると、Herokuサポートに書いてありました。
(和訳後引用)
Heroku は、ルートディレクトリにrequirements.txtまたはsetup.pyファイルが含まれている場合、そのアプリケーションを自動的にPythonアプリケーションとして認識します。
ということで原因が分かりました。
原因 | 対策 |
---|---|
requirements.txtの欠如 | requirements.txtをルートに追加 |
ちなみに内容はこんな感じです。
適宜増やしたし減らしたりしてください。
(バージョンは自分の実行環境に合わせてください。)
Flask==1.0.2
line-bot-sdk==1.8.0
urllib3==1.23
Pythonのバージョンは指定なしだとpython-3.6.8
らしい。
今回はpython-3.6.5
なので、次のファイルも一緒にアップロードしておきます。
puthon-3.6.5
また、HerokuのDyno設定をするため以下ファイルも必要です。
web: python main.py
実際にアプリを実行してみる
「Settings」タブの"Domains and certificates" 欄からURLをクリックすると
###Not Found
The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
なぜだか分からず、、、Heroku CLIからログをみてみよう。
Herokuプロキシ設定・・・プロキシ使っている人は設定忘れずに。
at=error code=H10 desc="App crashed" method=POST path="/callback" host={Myapp}.herokuapp.com request_id=***** fwd="203.104.146.155" dyno= connect= service= status=503 bytes= protocol=https
詳細をみるためheroku logs --tail
を実行した。
2019-03-02T01:14:36.783940+00:00 app[web.1]: File "main.py", line 64
2019-03-02T01:14:36.783966+00:00 app[web.1]:
2019-03-02T01:14:36.783967+00:00 app[web.1]: ^
2019-03-02T01:14:36.783968+00:00 app[web.1]: SyntaxError: unexpected EOF while parsing
あ、main.py
の最後の部分で)
が1個抜けてる、、、→修正しました。
よし。今度こそ!
2019-03-02T01:25:48.353864+00:00 app[web.1]: TextSendMessage(text=os.environ[res.getResponse(event.message.text)])
2019-03-02T01:25:48.353872+00:00 app[web.1]: TypeError: getResponse() missing 1 required positional argument: 'text'
またエラーですが、getResponse()
の引数が足りないらしい。
これインスタンス生成されてないと出ますよねー→インスタンス生成されてませんでした!!
コード書いてみた(修正後)
というわけでコード修正しました。
これでエラーなく動くようになりました。
# -*- coding: utf-8 -*-
import os
import sys
from account_response import Response
from flask import Flask, request, abort
from linebot import (
LineBotApi, WebhookHandler
)
from linebot.exceptions import (
InvalidSignatureError
)
from linebot.models import (
MessageEvent, TextMessage, TextSendMessage,
)
app = Flask(__name__)
-----追加-----
# インスタンス生成
res = Response()
---ここまで---
# Herokuの変数からトークンなどを取得
channel_secret = os.environ['LINE_CHANNEL_SECRET']
channel_access_token = os.environ['LINE_CHANNEL_ACCESS_TOKEN']
if channel_secret is None:
print('Specify LINE_CHANNEL_SECRET as environment variable.')
sys.exit(1)
if channel_access_token is None:
print('Specify LINE_CHANNEL_ACCESS_TOKEN as environment variable.')
sys.exit(1)
line_bot_api = LineBotApi(channel_access_token)
handler = WebhookHandler(channel_secret)
# LINEからのWebhook
@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)
# 署名を検証し、問題なければhandleに定義されている関数を呼び出す。
try:
handler.handle(body, signature)
except InvalidSignatureError:
abort(400)
return 'OK'
# LINEでMessageEvent(普通のメッセージを送信された場合)が起こった場合
# reply_messageの第一引数のevent.reply_tokenは、イベントの応答に用いるトークンです。
# 第二引数には、linebot.modelsに定義されている返信用のTextSendMessageオブジェクトを渡しています。
@handler.add(MessageEvent, message = TextMessage)
def handle_message(event):
#入力された内容(event.message.text)に応じて返信する
line_bot_api.reply_message(
event.reply_token,
TextSendMessage(text = os.environ[res.getResponse(event.message.text)])
)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=int(os.getenv("PORT", 5000)))--> ) 足した
# -*- coding: utf-8 -*-
class Response:
"""
dic: Herokuに登録してある変数
key以外の入力があったら、count+=1する
(Heroku上で1~4までのkeyに対応する変数を登録済み)
"""
dic={"会社概要":"COMPANY",
"事業内容":"SERVICE",
"採用情報":"CAREER",
"コマンド":"COMMAND",
"おはよう":"5",
"こんにちは":"5",
"こんばんは":"5",
"こんにちわ":"6",
"こんばんわ":"6"
}
count = 0
def getResponse(self,text):
if self.count >= 4:
self.count = 0
self.count += 1
for _dic in self.dic:
if _dic == text:
return self.dic[text]
return str(self.count) -->intからstrへ
記事には書いてないが、intで返すなと怒られました、、
まとめ
はじめて触って動かしてみると楽しかった。
簡単な動作だけですが、実際に動かすとなると相応時間と苦労がありました。
次はYoutubeで動画がアップロードされたら、LINEでユーザーに通知できるものを作ろう!