専用アプリはもう要らない?LINE BotからAkerunを操作する(その3)

この記事は Akerun Advent Calendar 2021 - Qiita の2日目の記事です。

WebエンジニアのBunです。主にiOSアプリの開発を担当しています。

2019年と2020のAdvent Calendarでは、「LINE BotからAkerunを操作する(その1)と(その2)」を書かせていただきました。 この2回でご紹介したAkerun APIの基本機能(施錠解錠)とLINE Bot(基本機能、LINEログイン、グループ機能)を連携すれば、十分使えると思いますが、今年の3回目では、Akerun APIから取得した事業所/Akerun/合鍵/ユーザー情報をLINE Botの少しリッチな表示機能のカルーセル表示についてご紹介します。

akerun.hateblo.jp

akerun.hateblo.jp

事前準備

LINE Botの設定とAkerun APIアクセストークンの生成

その1を参考にLINE Botを作成し、Botと友達になります。

アクセストークンについては、弊社のAkerun Developersをご参照ください。

developers.akerun.com

Akerun API

以下のAkerun APIを使って、事業所一覧、事業所詳細、Akerun一覧、Akerun詳細、合鍵一覧、合鍵詳細、ユーザー一覧、ユーザー詳細を取得します。

Akerun APIの詳細についてAkerun Developersをご参照ください。

事業所一覧
GET https://api.akerun.com/v3/organizations

事業所詳細
GET https://api.akerun.com/v3/organizations/{ORGANIZATION_ID}

Akerun一覧
GET https://api.akerun.com/v3/organizations/{ORGANIZATION_ID}/akeruns

Akerun詳細
GET https://api.akerun.com/v3/organizations/{ORGANIZATION_ID}/akeruns/{AKERUN_ID}

合鍵一覧
GET https://api.akerun.com/v3/organizations/{ORGANIZATION_ID}/keys

合鍵詳細
GET https://api.akerun.com/v3/organizations/{ORGANIZATION_ID}/keys/{KEY_ID}

ユーザー一覧
GET https://api.akerun.com/v3/organizations/{ORGANIZATION_ID}/users

ユーザー詳細
GET https://api.akerun.com/v3/organizations/{ORGANIZATION_ID}/users/{USER_ID}

pythonのコードは下記になります。

def akerun_bot_get_organizations(akerun_token):
  ak_url = 'https://api.akerun.com/v3/organizations'
  ak_headers = {'Authorization': 'Bearer ' + akerun_token}
  resp_data = akerun_bot_api_get(ak_url, ak_headers)
  return resp_data["organizations"] if resp_data is not None else []

def akerun_bot_get_organization_info(akerun_token, org_id):
  ak_url = 'https://api.akerun.com/v3/organizations/{}'.format(org_id)
  ak_headers = {'Authorization': 'Bearer ' + akerun_token}
  resp_data = akerun_bot_api_get(ak_url, ak_headers)
  return resp_data["organization"] if resp_data is not None else None

def akerun_bot_get_akeruns(akerun_token, org_id):
  ak_url = 'https://api.akerun.com/v3/organizations/{}/akeruns'.format(org_id)
  ak_headers = {'Authorization': 'Bearer ' + akerun_token}
  resp_data = akerun_bot_api_get(ak_url, ak_headers)
  return resp_data["akeruns"] if resp_data is not None else []

def akerun_bot_get_akerun_info(akerun_token, org_id, ak_id):
  ak_url = 'https://api.akerun.com/v3/organizations/{}/akeruns/{}'.format(org_id, ak_id)
  ak_headers = {'Authorization': 'Bearer ' + akerun_token}
  resp_data = akerun_bot_api_get(ak_url, ak_headers)
  return resp_data["akerun"] if resp_data is not None else None

def akerun_bot_get_keys(akerun_token, org_id):
  ak_url = 'https://api.akerun.com/v3/organizations/{}/keys'.format(org_id)
  ak_headers = {'Authorization': 'Bearer ' + akerun_token}
  resp_data = akerun_bot_api_get(ak_url, ak_headers)
  return resp_data["keys"] if resp_data is not None else []

def akerun_bot_get_key_info(akerun_token, org_id, key_id):
  ak_url = 'https://api.akerun.com/v3/organizations/{}/keys/{}'.format(org_id, key_id)
  ak_headers = {'Authorization': 'Bearer ' + akerun_token}
  resp_data = akerun_bot_api_get(ak_url, ak_headers)
  return resp_data["key"] if resp_data is not None else None

def akerun_bot_get_users(akerun_token, org_id):
  ak_url = 'https://api.akerun.com/v3/organizations/{}/users'.format(org_id)
  ak_headers = {'Authorization': 'Bearer ' + akerun_token}
  resp_data = akerun_bot_api_get(ak_url, ak_headers)
  return resp_data["users"] if resp_data is not None else []

def akerun_bot_get_user_info(akerun_token, org_id, user_id):
  ak_url = 'https://api.akerun.com/v3/organizations/{}/users/{}'.format(org_id, user_id)
  ak_headers = {'Authorization': 'Bearer ' + akerun_token}
  resp_data = akerun_bot_api_get(ak_url, ak_headers)
  return resp_data["user"] if resp_data is not None else None

def akerun_bot_api_get(url, headers):
  obj_session = Session()
  obj_request = Request("GET",
              url,
              headers=headers
              )
  obj_prepped = obj_session.prepare_request(obj_request)
  obj_response = obj_session.send(obj_prepped,
                  verify=True,
                  timeout=60
                  )
  
  print('status_code:' + str(obj_response.status_code))
  print('obj_response.text:' + obj_response.text)
  if obj_response.status_code == 200:
    response_dict = json.loads(obj_response.text)
    print(response_dict)
    return response_dict

LINE Bot(Messaging API)のテンプレートメッセージ

今回はテンプレートメッセージのカルーセルと画像カルーセルを使って、事業所とAkerunを少しリッチに表示します。

developers.line.biz

事業所一覧(カルーセルテンプレート)

Akerunのユーザーは複数の事業所に所属できるので、事業所一覧を横スクロール可能なカルーセルテンプレートで表示します。

ルーセルテンプレートは複数のカラムを表示するテンプレートです。カラムは横にスクロールして順番に表示できます。

複数のActionも設定可能なので、事業所のAkerun一覧、合鍵一覧、ユーザー一覧を取得するActionも追加可能です。

事業所一覧取得メニューをTapしたら、Akerun APIから事業所一覧と詳細を取得し、カルーセルテンプレートで返すようにしています。

※今回は適当に「organizations」文字列で代替していますが、その1を参考にリッチメニューにメニューを追加すれば良いと思います。

# TextMessageとして「organizations」が通知される
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
  reply_msgs = []
  if event.message.type == 'text':
      cmd = event.message.text
      # 環境変数から取得したTokenを使用
      akerun_token = akerun_temp_token
      if cmd == 'organizations':
        organizations = akerun_bot_get_organizations(akerun_token)
        org_action_columns = []
        for org in organizations:
          org_id = org['id']
          if org_info is not None:
            org_name = org_info['name']
            org_id = org_info['id']
            org_action_columns.append(
              CarouselColumn(
                title = org_name,
                text = u'事業所の詳細情報を取得',
                default_action = PostbackTemplateAction(
                  label=u'Akerun一覧を取得',
                  data=json.dumps({'org_id': org_id})
                ),
                actions = [
                  PostbackTemplateAction(
                    label=u'Akerun一覧を取得',
                    data=json.dumps({'cmd': 'get_akeruns', 'org_id': org_id})
                  ),
                  PostbackTemplateAction(
                    label=u'鍵一覧を取得',
                    data=json.dumps({'cmd': 'get_keys', 'org_id': org_id})
                  ),
                  PostbackTemplateAction(
                    label=u'ユーザー一覧を取得',
                    data=json.dumps({'cmd': 'get_users', 'org_id': org_id})
                  ),
                ]
              )
            )

            # 最大10個
            if len(akerun_action_columns) >= 10:
              break

        if len(org_action_columns) > 0:
          reply_msgs.append(
            TemplateSendMessage(
              alt_text=u'事業所一覧',
              template=CarouselTemplate(
                columns = org_action_columns
              )
            )
          )
        else:
          reply_msgs.append(
            TextSendMessage(text = u'所属している事業所がありません。')
          )

      if len(reply_msgs) > 0:
        line_bot_api.reply_message(event.reply_token, reply_msgs)

Akerun一覧(画像カルーセルテンプレート)

Akerun一覧は横スクロール可能な画像カルーセルテンプレートで表示します。

画像カルーセルテンプレートは複数の画像を表示するテンプレートです。画像は横にスクロールして順番に表示できます。

事業所一覧のカルーセルのActionではPostbackTemplateActionを使ったので、Akerun一覧をTapした時のイベントはPostbackEventで拾います。

@handler.add(PostbackEvent)
def handle_postback_event(event):
  uid = get_id(event.source)
  data = json.loads(event.postback.data)
  cmd = data.get('cmd')
  reply_msgs = []

  if cmd == 'get_akeruns':
    akerun_token = akerun_temp_token
    org_id = data.get('org_id')
    akeruns = akerun_bot_get_akeruns(akerun_token, org_id)
    akerun_action_columns = []
    for ak in akeruns:
      ak_id = ak['id']
      ak_name = ak['name']
      ak_imgage = ak['image_url']
      ak_action = PostbackTemplateAction(
        label=org_name,
        data=json.dumps({'cmd': 'select_akerun', 'value': 'token={}&org_id={}&ak_id'.format(akerun_token, org_id, ak_id)})
      )
      akerun_action_columns.append(
        ImageCarouselColumn(
          image_url = ak_imgage,
          action = PostbackTemplateAction(
              # 12文字超える
              # label=ak_name + u'を解錠',
              label=ak_id + u'を解錠',
              data=json.dumps({'cmd': 'unlock', 'org_id': org_id, 'ak_id': ak_id})
          )
        )
      )

      # 最大10個
      if len(akerun_action_columns) >= 10:
        break
    
    if len(akerun_action_columns) > 0:
      reply_msgs.append(
        TemplateSendMessage(
          alt_text=u'Akerun一覧',
          template=ImageCarouselTemplate(
            columns=akerun_action_columns
          )
        )
      )
    else:
      reply_msgs.append(
        TextSendMessage(text = u'使用可能なAkerunがありません。')
      )
    
    if len(reply_msgs) > 0:
      line_bot_api.reply_message(event.reply_token, reply_msgs)

ただ、画像カルーセルテンプレートはカルーセルテンプレートと違って、画像が全領域に表示されて、Actionも一つしか設定できない(施錠か解錠しかできない)ので、カルーセルテンプレートの方が良さそうです。

上記のImageCarouselTemplateの部分をCarouselTemplateに変更します。

akerun_action_columns.append(
  CarouselColumn(
    title = ak_name,
    text = u'施錠解錠',
    thumbnail_image_url = ak_imgage,
    default_action = PostbackTemplateAction(
      label=u'解錠',
      data=json.dumps({'cmd': 'unlock', 'org_id': org_id, 'ak_id': ak_id})
    ),
    actions = [
      PostbackTemplateAction(
        label=u'施錠',
        data=json.dumps({'cmd': 'lock', 'org_id': org_id, 'ak_id': ak_id})
      ),
      PostbackTemplateAction(
        label=u'解錠',
        data=json.dumps({'cmd': 'unlock', 'org_id': org_id, 'ak_id': ak_id})
      )
    ]
  )
)


reply_msgs.append(
  TemplateSendMessage(
    alt_text=u'Akerun一覧',
    # template=ImageCarouselTemplate(
    #   columns=akerun_action_columns
    # )
    template=CarouselTemplate(
      columns = akerun_action_columns
    )
  )
)

施錠解錠

Akerun一覧のカルーセルのActionもPostbackTemplateActionを使ったので、施錠解錠をTapした時のイベントはPostbackEventで拾います。

施錠解錠の詳細について、その1とその2をご参照ください。

@handler.add(PostbackEvent)
def handle_postback_event(event):
  uid = get_id(event.source)
  data = json.loads(event.postback.data)
  cmd = data.get('cmd')
  reply_msgs = []

  if cmd == 'get_akeruns':
    # akerun一覧
  elif cmd == 'unlock':
    profile = line_bot_api.get_profile(event.source.user_id)
    name = profile.display_name
    push_msgs = []
    org_id = data.get('org_id')
    ak_id = data.get('ak_id')
    job_id = akerun_bot_unlock(akerun_temp_token, org_id, ak_id)
    reply_msgs.append(TextSendMessage(text=u'解錠'))
    push_msgs.append(TextSendMessage(text = u'{}さんが施錠しました'.format(name)))
    # todo:pooling -> push
    if len(reply_msgs) > 0:
      line_bot_api.reply_message(event.reply_token, reply_msgs)
    if len(push_msgs) > 0:
      line_bot_api.push_message(test_group_id, push_msgs)

合鍵一覧とユーザー一覧

合鍵一覧とユーザー一覧取得APIを使えば、後はAkerun一覧と同じ方法で表示できるので、詳細については割愛します。

まとめ

  • LINE Messaging APIではテキストだけではなく、カルーセルテンプレートのようなもっとリッチな表示が可能で、対応方法も簡単です。

  • シンプルに画像をTapした時のActionを実現する場合は画像カルーセルテンプレート、画像も表示しつつ複数のActionを実現する場合はカルーセルテンプレートを使えば良さそうです。

  • テンプレートMessage以外にも、画像、動画、音声、位置情報、イメージマップMessageとLayoutを自由にカスタマイズ可能なFlex Messageもあります。

  • Akerun APIには合鍵発行、ユーザー登録、ICカード登録、施錠解錠履歴取得などの機能もあるので、上記Messageを使ってみたいと思います。


株式会社フォトシンスでは、一緒にプロダクトを成長させる様々なレイヤのエンジニアを募集しています。 hrmos.co

Akerun Proの購入はこちらから akerun.com