LINE BotでFlex Messageを使ってみる（Python編）

#目的
LINE Botではただのテキストメッセージだけではなく、Flex Messageというリッチなコンテンツをレスポンスとして返せるようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495652/69d30637-e9ec-627c-ab90-11c91b144165.png)
こんな感じのやつです。


Pythonでこれを実現している記事が少なかったので自分なりのメモがてら書いてみます。

#やったこと
##LINE BOTを用意する
こちらは今度別記事で書こうと思います。
登録後、発行される「ACCESS_TOKEN」をメモしておきましょう。コード内で使います。

##Pythonコードを用意する
まずは、モジュールをインストールします。

```shell
pip install line-bot-sdk
```
これでとりあえずインストールは完了です。

次に、コードを解説していきます。
###コード序盤部分（importなど事前準備）

```python:push.py
#linebotapiのインポート
from linebot import LineBotApi
from linebot.models import TextSendMessage,  FlexSendMessage

#LineBotApiインスタンスの生成
#これ以降、line_bot_apiオブジェクト内のメソッドを呼び出すことで、メッセージの送信が可能になります
line_bot_api = LineBotApi('LINE Developersページで発行されたACCESS_TOKEN')
```

###Flex Messageの準備
Botが返すFlex MessageはJSON形式で記述するのですが、あまりにも複雑で自力で書くのはおすすめしません。公式に<a href="https://developers.line.biz/ja/services/bot-designer/">Line Bot Designer</a>というデスクトップアプリケーションが用意されているので、これを利用するのが楽です。

アプリケーションをインストール後、起動するとログインを求められるので、ここは自分のLINEアカウントでログインしてください。
その後、こんな画面が出ますので「プロジェクトを作成」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495652/596def04-6e2f-afa8-0c72-edf366eaad38.png)
「プロジェクトの設定」ウィンドウが開きますが、適当で構いません。

するとこんな画面が開きますが、この画面では様々なメッセージやリッチメニュー、Flex MessageをGUI上で生成することができます。（なにげにエミュレータもついててすごい・・・）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495652/a1b4e962-f172-e170-d00d-f4c018c43be0.png)

さて、今回のターゲットは「Flex Message」なので、左側の3つのアイコンのうち真ん中のアイコンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495652/76936220-01bb-04e7-a51f-a51085600566.png)

試しに、バブルメッセージを作ってみます。
「バブルメッセージ」の横の＋ボタンをクリックすると、こんな感じになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495652/8b49ec3c-19ae-90cd-350d-4e0c8944c508.png)

適当にテンプレートを選んでみます。今回はレストランにしてみました。
勝手に、このようにJSONが生成されるのでこれをコピーしておきます。これがプレビュー部分に表示されているFlex Messageを実現するためのJSONになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495652/0d6b35ff-77c5-819c-c411-1b3ffc7680a3.png)

###コード中盤部分
適当な変数を作り、先程コピーしておいたJSONコードを代入します。
ここで注意点です。真偽値の部分が「true」と書かれており、JavaScript等であればそのままで良いのでしょうけど、Pythonでは真偽値は「True/False」と大文字で始まるため、そこだけ書き換える必要があります。
エディタの置換機能などを使って書き換えてしまうと良いでしょう。

```python:push.py
payload = {
  "type": "flex",
  "altText": "Flex Message",
  "contents": {
    "type": "bubble",
    "hero": {
      "type": "image",
      "url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_1_cafe.png",
      "size": "full",
      "aspectRatio": "20:13",
      "aspectMode": "cover",
      "action": {
        "type": "uri",
        "label": "Line",
        "uri": "https://linecorp.com/"
      }
    },
    "body": {
      "type": "box",
      "layout": "vertical",
      "contents": [
        {
          "type": "text",
          "text": "Brown Cafe",
          "size": "xl",
          "weight": "bold"
        },
        {
          "type": "box",
          "layout": "baseline",
          "margin": "md",
          "contents": [
            {
              "type": "icon",
              "url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png",
              "size": "sm"
            },
            {
              "type": "icon",
              "url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png",
              "size": "sm"
            },
            {
              "type": "icon",
              "url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png",
              "size": "sm"
            },
            {
              "type": "icon",
              "url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png",
              "size": "sm"
            },
            {
              "type": "icon",
              "url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gray_star_28.png",
              "size": "sm"
            },
            {
              "type": "text",
              "text": "4.0",
              "flex": 0,
              "margin": "md",
              "size": "sm",
              "color": "#999999"
            }
          ]
        },
        {
          "type": "box",
          "layout": "vertical",
          "spacing": "sm",
          "margin": "lg",
          "contents": [
            {
              "type": "box",
              "layout": "baseline",
              "spacing": "sm",
              "contents": [
                {
                  "type": "text",
                  "text": "Place",
                  "flex": 1,
                  "size": "sm",
                  "color": "#AAAAAA"
                },
                {
                  "type": "text",
                  "text": "Miraina Tower, 4-1-6 Shinjuku, Tokyo",
                  "flex": 5,
                  "size": "sm",
                  "color": "#666666",
                  "wrap": True
                }
              ]
            },
            {
              "type": "box",
              "layout": "baseline",
              "spacing": "sm",
              "contents": [
                {
                  "type": "text",
                  "text": "Time",
                  "flex": 1,
                  "size": "sm",
                  "color": "#AAAAAA"
                },
                {
                  "type": "text",
                  "text": "10:00 - 23:00",
                  "flex": 5,
                  "size": "sm",
                  "color": "#666666",
                  "wrap": True
                }
              ]
            }
          ]
        }
      ]
    },
    "footer": {
      "type": "box",
      "layout": "vertical",
      "flex": 0,
      "spacing": "sm",
      "contents": [
        {
          "type": "button",
          "action": {
            "type": "uri",
            "label": "CALL",
            "uri": "https://linecorp.com"
          },
          "height": "sm",
          "style": "link"
        },
        {
          "type": "button",
          "action": {
            "type": "uri",
            "label": "WEBSITE",
            "uri": "https://linecorp.com"
          },
          "height": "sm",
          "style": "link"
        },
        {
          "type": "spacer",
          "size": "sm"
        }
      ]
    }
  }
}
```
これでJSONコードが出来上がりました。

###コード終盤部分（メッセージの送信）
今回は、pushでメッセージを送ってみました。
line_bot_api.push_message()メソッドを使っていますが、メッセージへの応答にするためには
line_bot_api.reply_message()メソッドを使えばOKです。
reply_message()の引数は、REPLY_TOKEN(LINE Developersで発行可能）とメッセージオブジェクト（下のコードだとcontainer_obj変数）になります。

```python:push.py
#new_from_json_dictメソッドはJSONデータをFlexMessage等各種オブジェクトに変換してくれるメソッドです
#FlexSendMessage.new_from_json_dict(対象のJSONデータ）とすることで、
#FlexSendMessage型に変換されます
container_obj = FlexSendMessage.new_from_json_dict(payload)

#最後に、push_messageメソッドを使ってPUSH送信する
line_bot_api.push_message('送りたい相手のUserID', messages=container_obj)
```

#まとめ
というわけで、下記コードが完成品になります。

```python:push.py
from linebot import LineBotApi
from linebot.models import TextSendMessage,  FlexSendMessage

line_bot_api = LineBotApi('発行されたCHANNEL_ACCESS_TOKEN')

payload = {
  "type": "flex",
  "altText": "Flex Message",
  "contents": {
    "type": "bubble",
    "hero": {
      "type": "image",
      "url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/01_1_cafe.png",
      "size": "full",
      "aspectRatio": "20:13",
      "aspectMode": "cover",
      "action": {
        "type": "uri",
        "label": "Line",
        "uri": "https://linecorp.com/"
      }
    },
    "body": {
      "type": "box",
      "layout": "vertical",
      "contents": [
        {
          "type": "text",
          "text": "Brown Cafe",
          "size": "xl",
          "weight": "bold"
        },
        {
          "type": "box",
          "layout": "baseline",
          "margin": "md",
          "contents": [
            {
              "type": "icon",
              "url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png",
              "size": "sm"
            },
            {
              "type": "icon",
              "url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png",
              "size": "sm"
            },
            {
              "type": "icon",
              "url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png",
              "size": "sm"
            },
            {
              "type": "icon",
              "url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gold_star_28.png",
              "size": "sm"
            },
            {
              "type": "icon",
              "url": "https://scdn.line-apps.com/n/channel_devcenter/img/fx/review_gray_star_28.png",
              "size": "sm"
            },
            {
              "type": "text",
              "text": "4.0",
              "flex": 0,
              "margin": "md",
              "size": "sm",
              "color": "#999999"
            }
          ]
        },
        {
          "type": "box",
          "layout": "vertical",
          "spacing": "sm",
          "margin": "lg",
          "contents": [
            {
              "type": "box",
              "layout": "baseline",
              "spacing": "sm",
              "contents": [
                {
                  "type": "text",
                  "text": "Place",
                  "flex": 1,
                  "size": "sm",
                  "color": "#AAAAAA"
                },
                {
                  "type": "text",
                  "text": "Miraina Tower, 4-1-6 Shinjuku, Tokyo",
                  "flex": 5,
                  "size": "sm",
                  "color": "#666666",
                  "wrap": True
                }
              ]
            },
            {
              "type": "box",
              "layout": "baseline",
              "spacing": "sm",
              "contents": [
                {
                  "type": "text",
                  "text": "Time",
                  "flex": 1,
                  "size": "sm",
                  "color": "#AAAAAA"
                },
                {
                  "type": "text",
                  "text": "10:00 - 23:00",
                  "flex": 5,
                  "size": "sm",
                  "color": "#666666",
                  "wrap": True
                }
              ]
            }
          ]
        }
      ]
    },
    "footer": {
      "type": "box",
      "layout": "vertical",
      "flex": 0,
      "spacing": "sm",
      "contents": [
        {
          "type": "button",
          "action": {
            "type": "uri",
            "label": "CALL",
            "uri": "https://linecorp.com"
          },
          "height": "sm",
          "style": "link"
        },
        {
          "type": "button",
          "action": {
            "type": "uri",
            "label": "WEBSITE",
            "uri": "https://linecorp.com"
          },
          "height": "sm",
          "style": "link"
        },
        {
          "type": "spacer",
          "size": "sm"
        }
      ]
    }
  }
}

container_obj = FlexSendMessage.new_from_json_dict(payload)

line_bot_api.push_message('送りたい相手のUserID', messages=container_obj)
```

上記コードを実行したところ、無事送られてきたのが確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/495652/100baf89-88d8-3c4e-5bc8-3d2156c27191.png)

というわけで以上です。
何か不明点あればコメント頂けると嬉しいです！