💥

Lambda,DynamoDB,PythonでLINE Botのリッチメニュー を活用したアプリを開発したので学んだことを記す

2021/05/25に公開

リッチメニューとは何か

リッチメニューはLINE公式アカウントのトーク画面下部に表示されるメニュー機能。
リッチメニューの各領域にリンクを設定することで、LINE公式アカウントの各機能のほか、外部サイトや予約ページなどにユーザーを誘導することができる。

リッチメニューオブジェクト

json
{
  "size": {
    "width": 2500,
    "height": 1686
  },
  "selected": false,
  "name": "Nice richmenu",
  "chatBarText": "Tap to open",
  "areas": [
    {
      "bounds": {
        "x": 0,
        "y": 0,
        "width": 2500,
        "height": 1686
      },
      "action": {
        "type": "postback",
        "data": "action=buy&itemid=123"
      }
    }
  ]
}

リッチメニューの作成

shell
curl -v -X POST https://api.line.me/v2/bot/richmenu \
-H 'Authorization: Bearer {channel access token}' \
-H 'Content-Type: application/json' \
-d \
'{
    "size": {
      "width": 2500,
      "height": 1686
    },
    "selected": false,
    "name": "Nice richmenu",
    "chatBarText": "Tap here",
    "areas": [
      {
        "bounds": {
          "x": 0,
          "y": 0,
          "width": 2500,
          "height": 1686
        },
        "action": {
          "type": "postback",
          "data": "action=buy&itemid=123"
        }
      },
      {
        "bounds": {
          "x": 0,
          "y": 840,
          "width": 2500,
          "height": 1686
        },
        "action": {
          "type": "postback",
          "data": "action=buy&itemid=123"
        }
      }
   ]
}'

注意

リッチメニューの画像は1枚である

例えば、リッチメニューで6枚のタイルを設置したい場面においても、画像自体は1枚でなければいけない。(6枚アップロードするわけではない。)
なので私は、Figmaで6枚の画像を1枚のFieldに設置してExportして1枚の画像にしました!

リッチメニューの各コンテンツはタップ領域を決めなければいけない

上で、リッチメニューの画像は1枚であると述べたように、1枚の画像をアップロードしなければいけないため、複数のタイルを表示したいときは、画像のどの領域をタップすればどういったアクションを呼ぶかというものを定義しないといけない。

各領域のタップ領域は、各タイルの左上を基準にして定める

下に添付した画像の通り、画像の赤丸の領域(つまりタイルの角)が、1枚の画像に対してx座標(画像の上端からの距離),y座標(画像の左端からの距離)のどの位置に位置するかを定義する必要がある。

以下の画像について考えるとき、例えば、左端上のタイルのタップ領域を定義するには以下のようになる。左上のタイルの左上の角は1枚の画像の左端から0距離だからx=0,1枚の画像の上端から0距離なのでy=0で定義しなければいけない。

左上の定義
shell
curl -v -X POST https://api.line.me/v2/bot/richmenu \
-H 'Authorization: Bearer {channel access token}' \
-H 'Content-Type: application/json' \
-d \
'{
    "size": {
      "width": 2500,
      "height": 1686
    },
    "selected": false,
    "name": "Nice richmenu",
    "chatBarText": "Tap here",
    "areas": [
      {
        "bounds": {
          "x": 0,
          "y": 0,
          "width": 2500,
          "height": 1686
        },
        "action": {
          "type": "postback",
          "data": "action=buy&itemid=123"
        }
      }
   ]
}'

同様にすると

右下の定義
shell
curl -v -X POST https://api.line.me/v2/bot/richmenu \
-H 'Authorization: Bearer {channel access token}' \
-H 'Content-Type: application/json' \
-d \
'{
    "size": {
      "width": 2500,
      "height": 1686
    },
    "selected": false,
    "name": "Nice richmenu",
    "chatBarText": "Tap here",
    "areas": [
      {
        "bounds": {
          "x": 0,
          "y": 840,
          "width": 2500,
          "height": 1686
        },
        "action": {
          "type": "postback",
          "data": "action=buy&itemid=123"
        }
      }
   ]
}'
真ん中上の定義
shell
curl -v -X POST https://api.line.me/v2/bot/richmenu \
-H 'Authorization: Bearer {channel access token}' \
-H 'Content-Type: application/json' \
-d \
'{
    "size": {
      "width": 2500,
      "height": 1686
    },
    "selected": false,
    "name": "Nice richmenu",
    "chatBarText": "Tap here",
    "areas": [
      {
        "bounds": {
          "x": 830,
          "y": 0,
          "width": 2500,
          "height": 1686
        },
        "action": {
          "type": "postback",
          "data": "action=buy&itemid=123"
        }
      }
   ]
}'

複数のタイルを作りたいときは、areasの中身を配列にして渡せばOKです!

以下は6枚のタイルを作るとき。(実際はxとかyの値を変えてあげてくださいね!)

shell
curl -v -X POST https://api.line.me/v2/bot/richmenu \
-H 'Authorization: Bearer {channel access token}' \
-H 'Content-Type: application/json' \
-d \
'{
    "size": {
      "width": 2500,
      "height": 1686
    },
    "selected": false,
    "name": "Nice richmenu",
    "chatBarText": "Tap here",
    "areas": [
      {
        "bounds": {
          "x": 830,
          "y": 0,
          "width": 2500,
          "height": 1686
        },
        "action": {
          "type": "postback",
          "data": "action=buy&itemid=123"
        }
      },
      {
        "bounds": {
          "x": 830,
          "y": 0,
          "width": 2500,
          "height": 1686
        },
        "action": {
          "type": "postback",
          "data": "action=buy&itemid=123"
        },
        {
        "bounds": {
          "x": 830,
          "y": 0,
          "width": 2500,
          "height": 1686
        },
        "action": {
          "type": "postback",
          "data": "action=buy&itemid=123"
        },
        {
        "bounds": {
          "x": 830,
          "y": 0,
          "width": 2500,
          "height": 1686
        },
        "action": {
          "type": "postback",
          "data": "action=buy&itemid=123"
        },
        {
        "bounds": {
          "x": 830,
          "y": 0,
          "width": 2500,
          "height": 1686
        },
        "action": {
          "type": "postback",
          "data": "action=buy&itemid=123"
        },
        {
        "bounds": {
          "x": 830,
          "y": 0,
          "width": 2500,
          "height": 1686
        },
        "action": {
          "type": "postback",
          "data": "action=buy&itemid=123"
        }
   ]
}'

リッチメニューの配列を取得

shell
curl -v -X GET https://api.line.me/v2/bot/richmenu/list \
-H 'Authorization: Bearer {channel access token}'

リッチメニューを削除

shell
curl -v -X DELETE https://api.line.me/v2/bot/richmenu/{richMenuId} \
-H 'Authorization: Bearer {channel access token}'

リッチメニューの設定(公開)

shell
curl -v -X POST https://api.line.me/v2/bot/user/all/richmenu/{richMenuId} \
-H "Authorization: Bearer {channel access token}"

リッチメニューの画像をアップロード

shell
curl -v -X POST https://api-data.line.me/v2/bot/richmenu/{richMenuId}/content \
-H "Authorization: Bearer {channel access token}" \
-H "Content-Type: image/jpeg" \
-T image.jpg

PostBackオブジェクトのレスポンスの形式

json
{
   "type":"postback",
   "label":"Buy",
   "data":"action=buy&itemid=111",
   "text":"Buy"
}

よって、例えば、リッチメニューに設定したdataを取り出したいときは、

python
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,PostbackEvent,PostbackTemplateAction # PostbackEventの取得用
)

@handler.add(PostbackEvent)
def on_postback(event):
    # PostBackを取得
    postback_msg = event.postback.data

Webhookのイベントオブジェクト(何が返ってくるか)

json
{
  "destination": "xxxxxxxxxx",
  "events": [
    {
      "replyToken": "0f3779fba3b349968c5d07db31eab56f",
      "type": "message",
      "mode": "active",
      "timestamp": 1462629479859,
      "source": {
        "type": "user",
        "userId": "U4af4980629..."
      },
      "message": {
        "id": "325708",
        "type": "text",
        "text": "Hello, world"
      }
    },
    {
      "replyToken": "8cf9239d56244f4197887e939187e19e",
      "type": "follow",
      "mode": "active",
      "timestamp": 1462629479859,
      "source": {
        "type": "user",
        "userId": "U4af4980629..."
      }
    }
  ]
}

よって、メッセージを送ってきたクライアントUserのidを取得するには

python
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event): # 引数のeventにAPIからのレスポンスが入っている
    returnUserid = event.source.user_id

メッセージをユーザーに返す (MessageEvent)

python
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event): # 引数のeventにAPIからのレスポンスが入っている
    text = event.message.text # 送信されたメッセージを取得
    line_bot_api.reply_message(event.reply_token, TextSendMessage(text)) # トークンと送信テキストをTextSendMessage(text)にて指定。おうむ返しアプリを作りたければ、送信された内容と同じ内容を返すようにすればいいので上のようにすればいい。

注意

ReplyTokenは一回のみしか使えない

通称、応答トークンは一回しか使えないので、例えば以下のようにすると、先に書いた方のメッセージしかBotにてユーザーに返されない。

lambda_function.py
from linebot import LineBotApi
from linebot.models import TextSendMessage
from linebot.exceptions import LineBotApiError

line_bot_api = LineBotApi('<channel access token>')

try:
    line_bot_api.reply_message('<reply_token>', TextSendMessage(text='Hello World first!'))
    line_bot_api.reply_message('<reply_token>', TextSendMessage(text='Hello World second!'))
except LineBotApiError as e:

# => output is below
Hello World first!

特定のユーザー情報の取得

python
from linebot import LineBotApi
from linebot.exceptions import LineBotApiError

line_bot_api = LineBotApi('<channel access token>')

# UserId = event.source.user_idでユーザーIDを取得する
try:
    profile = line_bot_api.get_profile('<user_id>')
    user_disp_name = profile.display_name # アカウント名
except LineBotApiError as e:
    # error handle
    ...

返ってくるレスポンス

json
{
    "displayName":"LINE taro",
    "userId":"U4af4980629...",
    "language":"en",
    "pictureUrl":"https://obs.line-apps.com/...",
    "statusMessage":"Hello, LINE!"
}

Botを友達追加したユーザーのリストを取得する

shell
curl -v -X GET https://api.line.me/v2/bot/followers/ids?start={continuationToken} \
-H 'Authorization: Bearer {channel access token}'

返ってくるレスポンス

json
{
   "userIds":[
      "U4af4980629...",
      "U0c229f96c4...",
      "U95afb1d4df..."
   ],
   "next":"yANU9IA..."
}

参照

https://developers.line.biz/ja/reference/messaging-api

Discussion