雑食日誌

Vue.jsとServerless。ときどきチーム開発

line-bot-sdk-pythonを使ってみた

はじめに

line-bot-sdk-pythonPythonを用いてLINE botを作りました。 ライブラリの操作で何箇所か詰まってしまったので、備忘録として記載します。

開発環境

ライブラリ

概要

line-bot-sdk-pythonはLINEのMessaging APIのクライアントライブラリであり、Bot開発を簡単に始めることがきます。 デコレータを用いて、イベントごとの処理を分けて記述したり、Signatureから署名を検証する操作を簡単にかけます。

事前準備

初めてLINE Botを作成するときは以下の公式ドキュメントからチャネルの作成まで完了する必要があります。 登録が終わったら、自分のBotの各種設定をコンソール画面から取得することができます。

URL設定

LINEのWebhookを用いるとBotへのメッセージや友達追加などのイベント時にHTTP POSTリクエストを発行できます。 コンソール画面のWebhook URLの項目にPOST先のURLを定義することで設定が完了します。

Djangoではルーティングを用いてURLを設定します。

urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('callback', views.callback)
]

認証

LINE Messaging APIを用いいる場合は以下の二つの定義が必要になります。

  • LINE_ACCESS_SECRET: 署名の検証で使用
  • CHANNEL_ACCESS_TOKEN: 各API通信を行うときに使用

コンソール画面のChannel基本設定から、Channel Secretとアクセストークン を発行します。これらがそれぞれLINE_ACCESS_SECRETとCHANNEL_ACCESS_TOKENに当たります。

Djangoのviews.py上では以下になります。

views.py

from linebot import LineBotApi, WebhookHandler

# 各クライアントライブラリのインスタンス作成
line_bot_api = LineBotApi(channel_access_token=CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(channel_secret=LINE_ACCESS_SECRET)

handlerの使い方

レスポンスの関数呼び出しとリクエストの署名検証

リクエストごとの関数を呼び出すためにはWebhookHandlerのhandleメソッドを用います。 handleメソッドは署名検証も同時に行います。 引数にはレスポンスのbodyと署名検証のためのsignatureを使います。

views.py

from django.http import HttpResponseForbidden, HttpResponse
from linebot.exceptions import InvalidSignatureError

def callback(request):
    # リクエストヘッダーから署名検証のための値を取得
    signature = request.META['HTTP_X_LINE_SIGNATURE']
    # リクエストボディを取得
    body = request.body.decode('utf-8')
    try:
        # 署名を検証し、問題なければhandleに定義されている関数を呼び出す
        handler.handle(body, signature)
    except InvalidSignatureError:
        # 署名検証で失敗したときは例外をあげる
        HttpResponseForbidden()
    # handleの処理を終えればOK
    return HttpResponse('OK', status=200)

イベントごとに関数を定義

handlerのaddメソッドを用いて、リクエストのイベントごとに実行する関数を記述できます。 例えば、友達追加されたときに「初めまして」というメッセージを送信する処理は以下のようになります。

views.py

# linebot.modelsから処理したいイベントをimport
from linebot.models import (
    FollowEvent, TextSendMessage
)

# addメソッドの引数にはイベントのモデルを入れる
# 関数名は自由
@handler.add(FollowEvent)
def handle_follow(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text='初めまして')
    )

handler.addメソッドには引数にリクエストのイベントを指定します。 イベントはlinebot.modelsに定義されていて、メッセージやグループ参加などがあります。 関数の引数であるeventはLINE Messaging APIで定義されているリクエストボディになります。

リプライはLineBotApiのメソッドを用いています。 第一引数のevent.reply_tokenはイベントの応答に用いるトークンです。 第二引数にはlinebot.modelsに定義されている返信用のTextSendMessageオブジェクトを渡しています。

handler.addメソッドはメッセージイベントの場合にテキストや画像のようなメッセージの 内容でも処理を分けることができます。

from linebot.models import (
    MessageEvent,
    TextMessage ,ImageMessage, AudioMessage
)

# メッセージイベントの場合の処理
# かつテキストメッセージの場合
@handler.add(MessageEvent, message=TextMessage)
def handle_text_message(event):
    # メッセージでもテキストの場合はオウム返しする
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text)
    )


# 画像、音声メッセージの場合
@handler.add(MessageEvent, message=(ImageMessage, AudioMessage))
def handle_image_audio_message(event):
    content = line_bot_api.get_message_content(event.message.id)
    with open('file', 'w') as f:
        for c in content.iter_content():
            f.write(c)

まとめ

line-bot-sdk-pythonを使ってイベントごとにレスポンスを変える処理を作りました。 処理部分を関数で簡単に分けることができるためとてもシンプルに書くことができます。 この処理を使って複数のAPIを連携したbotを作ったので、後日まとめたいです。

コード例

views.py

from django.http import HttpResponseForbidden, HttpResponse

from linebot.exceptions import InvalidSignatureError
from linebot.models import (
    MessageEvent, TextMessage, FollowEvent, UnfollowEvent,
    TextSendMessage, ImageMessage, AudioMessage
)

from linebot import LineBotApi, WebhookHandler


# 各クライアントライブラリのインスタンス作成
line_bot_api = LineBotApi(channel_access_token=CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(channel_secret=LINE_ACCESS_SECRET)


def callback(request):
    # signatureの取得
    signature = request.META['HTTP_X_LINE_SIGNATURE']
    body = request.body.decode('utf-8')
    try:
        # 署名の検証を行い、成功した場合にhandleされたメソッドを呼び出す
        handler.handle(body, signature)
    except InvalidSignatureError:
        return HttpResponseForbidden()
    return HttpResponse('OK')



# フォローイベントの場合の処理
@handler.add(FollowEvent)
def handle_follow(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text='初めまして')
    )


# メッセージイベントの場合の処理
@handler.add(MessageEvent, message=TextMessage)
def handle_text_message(event):
    # メッセージでもテキストの場合はオウム返しする
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text)
    )

@handler.add(MessageEvent, message=(ImageMessage, AudioMessage))
def handle_image_audio_message(event):
    # 画像と音源の場合は保存する
    content = line_bot_api.get_message_content(event.message.id)
    with open('file', 'w') as f:
        for c in content.iter_content():
            f.write(c)

参考