PythonでLINEの「Messaging API」のクイックリプライの解説と実装

LINEは、日本でもっとも日常的に使われるアプリの一つであることは間違いないでしょう。このLINEのプラットフォームをうまく使いこなせれば、自分が作りたいサービスや発信したい情報を広めることができるでしょう。

そこで今回は、LINEの「Messaging API」のクイックリプライ機能を実装してみました。

クイックリプライは、LINE以外でも、GmailやFacebookのMessengerなどでも利用されている機能です。

LINE Developerの登録などの基本設定は、「[LINEの「Messaging API」でテンプレートメッセージを実装してみた](https://qiita.com/kotamatsuoka/items/c4e651f1cb6c4490f4b8)」の記事を参考にしてください。

クイックリプライ機能を実装したLINE Botの例です。ぜひ、使って遊んでみてください(๑•̀ㅂ•́)و✧
<a href="https://line.me/R/ti/p/%40qfx3178y"><img height="36" border="0" alt="友だち追加" src="https://scdn.line-apps.com/n/line_add_friends/btn/ja.png"></a>
(※ クイックリプライ機能以外の機能も実装されたLINE Botなので、予めご了承ください。)

## 目次
- クイックリプライを実装
- QuickReplyButtonクラスの詳細
- QuickReplyクラスの詳細
- おまけ

## 対象
- LINEのMessaging APIを使ってみたい人
- Messaging APIのテンプレートメッセージを実装したい人
- クイックリプライ機能を実装したい人


## クイックリプライを実装
[公式サイト](https://developers.line.biz/ja/docs/messaging-api/using-quick-reply/)には、クイックリプライについて、このような説明があります。

> クイックリプライボタンを設定したメッセージをボットから受信すると、メッセージのほかに、トーク画面の下部にそれらのボタンが表示されます。ボタンをタップするだけで、ボットからのメッセージに簡単に返信できます。

ユーザーの入力負担を減らしたり、こちらが予期した内容のみを返信してほしい時などに使えます。

では、早速、コードを書いていきましょう。
(冒頭にも書きましたが、LINE Developerの初期設定などは、[こちら](https://qiita.com/kotamatsuoka/items/c4e651f1cb6c4490f4b8)を参照ください。)

「QuickReply」と「QuickReplyButton」の詳細については、この後見ていきます。

```python:app.py
import os

from flask import Flask, request, abort
from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    MessageEvent, TextMessage, QuickReplyButton, MessageAction, QuickReply, TextSendMessage)

app = Flask(__name__)

LINE_CHANNEL_ACCESS_TOKEN = os.environ["LINE_CHANNEL_ACCESS_TOKEN"]
LINE_CHANNEL_SECRET = os.environ["LINE_CHANNEL_SECRET"]

line_bot_api = LineBotApi(LINE_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(LINE_CHANNEL_SECRET)


@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 response_message(event):
    language_list = ["Ruby", "Python", "PHP", "Java", "C"]

    items = [QuickReplyButton(action=MessageAction(label=f"{language}", text=f"{language}が好き")) for language in language_list]

    messages = TextSendMessage(text="どの言語が好きですか？",
                               quick_reply=QuickReply(items=items))

    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)
```

この記述に関してコミットして、プッシュしたら、実際にLINE Botにメッセージを送信して、確認してみてください。

```bash
$ git add .
$ git commit -m "クイックリプライ機能を実装"
$ git push heroku master
```

## QuickReplyButtonクラスの詳細
`linebot/models/send_messages.py`内に定義されているクラスです。

### actions
「デフォルトのアイコンがあるaction」と「デフォルトのアイコンがないaction」に分けられます。

デフォルトのアイコンがあるactionは、「"imageUrl"」を指定しなくてもアイコンが表示されます。
デフォルトのアイコンがないactionに、「"imageUrl"」をしてしなくでも、文字だけが表示されて、エラーにはなりません。

- デフォルトのアイコンがあるaction
    - カメラ
        - "action": {"type":"camera", "label":"Camera"}
    - カメラロール
        - "action": {"type":"cameraRoll", "label":"Camera roll"}
    - 位置情報
        - "action": {"type":"location", "label":"Location"}
- デフォルトのアイコンがないaction
    - ポストバック
        - "action": {"type":"postback", "label":"Buy", "data":"action=buy&itemid=111","text":"Buy"}
    - メッセージ
        - "action": {"type":"message", "label":"表示される文字列", "text":"ボタンが押された後に送信される文字列"}
    - 日付選択
        - "action":
    {"type":"datetimepicker",
    "label":"Select date",
    "data":"storeId=12345",
    "mode":"datetime",
    "initial":"2017-12-25t00:00",
    "max":"2018-01-24t23:59",
    "min":"2017-12-25t00:00"}

このように公式サイトには書いてありますが、QuickReplyButton()内で使用されている`get_actionメソッド`の中を見ると、`URIAction`も定義されている!!!
もしかしたら、URIActionも使えるかもしれません。是非試してみてください。

```python:linebot/models/actions.py
def get_action(action):
    """Get action."""
    action_obj = Base.get_or_new_from_json_dict_with_types(
        action, {
            'postback': PostbackAction,
            'message': MessageAction,
            'uri': URIAction,
            'datetimepicker': DatetimePickerAction,
            'camera': CameraAction,
            'cameraRoll': CameraRollAction,
            'location': LocationAction,
        }
    )
    return action_obj
```

## QuickReplyクラスの詳細
QuickReplyButtonクラス同様に`linebot/models/send_messages.py`内に定義されているクラスです。

QuickReplyクラス内を見てみると、`items`に渡されるのは、**QuickReplyButtonクラスのみ**です。
他のクラスを渡さないように注意です！！

```python:linebot/models/send_messages.py
class QuickReply(with_metaclass(ABCMeta, Base)):
    def __init__(self, items=None, **kwargs):
        super(QuickReply, self).__init__(**kwargs)

        new_items = []
        if items:
            for item in items:
                new_items.append(self.get_or_new_from_json_dict(
                    item, QuickReplyButton
                ))
        self.items = new_items
```

## おまけ
最後までお付き合いいただき、ありがとうございました。
少しでもお役に立てたのであれば、嬉しいです。

クイックリプライ機能を実装したLINE Botの例です。ぜひ、使って遊んでみてください(๑•̀ㅂ•́)و✧
<a href="https://line.me/R/ti/p/%40qfx3178y"><img height="36" border="0" alt="友だち追加" src="https://scdn.line-apps.com/n/line_add_friends/btn/ja.png"></a>
(※ クイックリプライ機能以外の機能も実装されたLINE Botなので、予めご了承ください。)

[Renttle](https://renttle.jp)というサービスを開発中です。ぜひ、使ってみて、レビューをください。