今日も窓辺でプログラム

外資系企業勤めのエンジニアが勉強した内容をまとめておくブログ

Python (Flask) を使って簡単なLINEのBotを作ってみる

はじめに

ここ最近新しいチームに参加し、仕事がバタバタしていて更新が滞ってしまっていました。
以前のチームとはかなり毛色や作業内容が異なるので、今回は新しいチームでの勉強も兼ねて記事を書いてみます。

今回は、LINEが提供しているMessaging APIを使って簡単なBotを作成してみます。
まずはサーバーを用意してMessaging APIをつなぐことが目的ですので、話しかけたときの返答は「オウム返し」をするだけにします。

環境

今回使用する環境をざっと箇条書きでまとめます。

  • Microsoft AzureのApp Services
  • Python 3.4.3
  • Flask

PythonやFlaskなどはある程度わかる前提で記事を書いていきます。

作成するシステム

LINEで作成したBot用のアカウントは、Messaging API経由で話しかけられたメッセージをサーバーに送信する機能があります(Webhook)。
そのWebhookを、Azure上で走らせたFlaskのサーバーで受け取り、またLINEのMessaging APIに返答を返していきます。

LINEのアカウントを用意する

LINE Business Centerに登録します。Business Centerにログインできたら、Messaging API Developer Trialに登録してアカウントを用意します。
登録後、LINE@ Managerというページのアカウント設定>Bot設定からWebhook送信を「利用する」に変更します。
f:id:kanohk:20170503185013p:plain

こうすると、今回のアカウントに話しかけると、その内容がMessaging APIの仕様に従って指定したURLに飛ばされるようになります。
少々紛らわしいのですが、同じページの「LINE Developersで設定する」というリンクをクリックすると、LINE developersという別のページに飛ばされます。
ここにある「Webhook URL」という箇所に、これから用意するサーバーのURLを指定します。

AzureにApp Servicesを用意する

https://portal.azure.com からAzureにログインし、適当なリソースグループを作成、その中にApp Serviceを用意します。
実際にApp Serviceを作成する画面では、Web Appという名前で表示されていました。
f:id:kanohk:20170503200215p:plain

Flaskを使ってサーバーを用意する

今はがっつりWindowsで開発しているので、Visual Studioのテンプレートを使ってしまいます。
ファイル > 新規作成 > プロジェクトから、Flask Web Projectを選択します。Python tools for Visual Studioを入れていないと、このテンプレートは出てこないかもしれません。
f:id:kanohk:20170503183508p:plain

テンプレートがロードされるのを確認したら、F5を押して実行してみます。すると、すぐにローカルにサーバーが立ち上がりflaskが実行されます。
今回はAPIでUIなどはいらないので、views.pyをこんな感じにシンプルにして動作確認。

from datetime import datetime
from flask import jsonify
from MadproBot import app

@app.route('/')
@app.route('/home')
def home():
    return jsonify({
        "message": "this endpoint is active"
    })

ローカルでの動作を確認したら、次はプロジェクトを右クリックして「公開」、続いて表示される画面では指示に従って先ほど作成したApp Serviceを指定します。
f:id:kanohk:20170503201658p:plain

公開はすぐに終わり、先ほど作成したApp ServiceのURLにアクセスすると、正しくJSONが返ってきていることが確認できます。

LINE Messaging APIからのWebhookを受け取る

FlaskのサーバーにLINEへのメッセージを受け取るエンドポイントを用意します。
詳しい使用は公式サイトのドキュメントにありますが、指定したURLにwebhook event objectの配列がPOSTで送られてきます。
このリクエストを受け取とるためのエンドポイントを以下のように追加しました。

@app.route('/webhook', methods=['POST'])
def webhook():
    reply_to_line(request.json)
    return '', 200, {}

受け取ったJSONのデータをreply_to_lineという後ほど定義する関数に渡し、LINEサーバーには200を返す、それだけの処理です。
本当はX-Line-Signatureの内容とチャンネルシークレットの値を使ってリクエストの署名検証を行わないといけないのですが、今回はその処理はすっ飛ばしてしまいます。

reply_to_lineの中身は、今回は軽い処理ですが将来的にはある程度時間のかかる処理になる可能性もあるので非同期処理を行いたいところです。CeleryというモジュールがFlaskとともに使われることがあるようなのですが、今回少し触っただけではうまくいきませんでした。。また後日挑戦してみます。

返答を作成してMessaging APIに返す

最後はLINEに返信するメッセージを作成します。ドキュメント通りのJSONを作成して、指定されたエンドポイントにPOSTしてあげるだけです。

def reply_to_line(body):
    for event in body['events']:
        responses = []

        replyToken = event['replyToken']
        type = event['type']
        
        if type == 'message':
            message = event['message']
            
            if message['type'] == 'text':
                # そのままオウム返し
                responses.append(LineReplyMessage.make_text_response(message['text']))
            else:
                # テキスト以外のメッセージにはてへぺろしておく
                responses.append(LineReplyMessage.make_text_response('てへぺろ'))

        # 返信する
        LineReplyMessage.send_reply(replyToken, responses)


class LineReplyMessage:
    ReplyEndpoint = 'https://api.line.me/v2/bot/message/reply'
    AccessToken = '<Put your access token>'

    @staticmethod
    def make_text_response(text):
        return {
            'type': 'text',
            'text': text
        }

    @staticmethod
    def send_reply(replyToken, messages):
        reply = {
            'replyToken': replyToken,
            'messages': messages
        }

        headers = {
            'Content-Type': 'application/json',
            'Authorization': 'Bearer {}'.format(LineReplyMessage.AccessToken)
        }

        requests.post(
            LineReplyMessage.ReplyEndpoint,
            data=json.dumps(reply),
            headers=headers)

話しかけてみましょう

f:id:kanohk:20170503220341p:plain

この通り、期待通りに動いてくれました。

Azureへの公開が非常に簡単なので、サーバーのややこしい設定をすることなく簡単にMessaging APIとやり取りができました。
実際のサービスとしてリリースしたり、ある程度の量のトラフィックをさばかないといけないような状況だとこんな適当なコードではだめだとは思いますが、とりあえず試してみたい方はこんな感じで自分のPythonコードとLINEアカウントをつないで、いろいろ試してみるのも良いのではないかと思います。


一応、最後にコードも置いておきます。このフォルダにある3つのスクリプトファイルを見ると、大体の動きがわかってもらえるかと思います。
https://github.com/kanoh-k/MadproBot/tree/d4eeea9fbe4185873ecd77e19f4506c8cfb6f38c/MadproBot/MadproBot