8
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

LINE WORKSAdvent Calendar 2022

Day 17

Flexible Template な🐈にゃんこボット🐈

Posted at

これは LINE WORKS Advent Calendar 2022 17日目の記事です。

LINE WORKS のトークボットをもっと華やかにしたい!
という時に便利そうな「フレキシブル テンプレート」を見つけたので紹介します。

そして、単に紹介するだけでは・・・という事で、例によってにゃんこネタを絡めたかったのでググったら、なんとも素敵なサービスを見つけました。これはフュージョンするしかない!

という欲にまみれた記事でございます。

猫 on LINE WORKS

「にゃんこ」と発言すると 5 匹の猫ちゃんを表示しつつ、好みの子に投票できるボットはいかが?おかわりもあるよ!

見ての通り、カルーセル表示ですが画像を全体に表示しつつ、半透明のボタンやテキストを置けたりと綺麗な見た目をつくれました。見てるだけでも楽しくなります。

では実装や詳細を早速みていきましょう。

フレキシブル テンプレート is 何?

トークメッセージの 1 つで、CSS Flexible Box を活用したレイアウトをカスタマイズできるものです。

実際のところは、ボタンや画像などの大きさや位置を 全て JSON の構造式 に起こして組み立てていきます。

サンプル
{
  "content": {
    "type": "flex",
    "altText": "にゃんこ投票",
    "contents": {
      "type": "carousel",
      "contents": [
        // エントリー No.1
        {
          "type": "bubble",
          "size": "micro",
          "body": {
            "type": "box",
            "layout": "vertical",
            "contents": [
              {
                "type": "image",
                "url": "【猫ちゃんの画像URL】",
                "size": "full",
                "aspectMode": "cover",
                "aspectRatio": "2:3",
                "gravity": "top"
              },
              {
                "type": "box",
                "layout": "vertical",
                "position": "absolute",
                "offsetStart": "0px",
                "offsetEnd": "0px",
                "offsetBottom": "0px",
                "paddingAll": "20px",
                "paddingTop": "18px",
                "backgroundColor": "#1e5a2ecc",
                "contents": [
                  {
                    "type": "box",
                    "layout": "vertical",
                    "spacing": "sm",
                    "height": "40px",
                    "borderWidth": "1px",
                    "cornerRadius": "4px",
                    "margin": "xxl",
                    "borderColor": "#ffffff",
                    "action": {
                      "type": "postback",
                      "label": "",
                      "data": "【ポストバックID】"
                    },
                    "contents": [
                      {
                        "type": "filler"
                      },
                      {
                        "type": "box",
                        "layout": "baseline",
                        "spacing": "sm",
                        "contents": [
                          {
                            "type": "filler"
                          },
                          {
                            "type": "text",
                            "text": "投票!",
                            "color": "#ffffff",
                            "flex": 0,
                            "offsetTop": "-2px",
                            "weight": "regular",
                            "margin": "sm"
                          },
                          {
                            "type": "filler"
                          }
                        ]
                      },
                      {
                        "type": "filler"
                      }
                    ]
                  }
                ]
              },
              {
                "type": "box",
                "layout": "vertical",
                "position": "absolute",
                "offsetTop": "18px",
                "offsetStart": "18px",
                "width": "75px",
                "height": "25px",
                "cornerRadius": "20px",
                "backgroundColor": "#ff334b90",
                "contents": [
                  {
                    "type": "text",
                    "text": "28j",
                    "color": "#ffffff",
                    "align": "center",
                    "size": "sm",
                    "offsetTop": "3px"
                  }
                ]
              }
            ]
          }
        },
        {
          // エントリー No.2
        },
        {
          // エントリー No.3
        },
        // 省略
      ]
    }
  }
}

これをゼロから書き起こすのは流石にしんどいので、それを補助してくれるオンラインツール「Flexible Template Simulator」があります。

今回作ったボットも元々あるサンプルをベースに好みの大きさや配置に手直しして JSON の雛形をつくりました。後は繰り返し処理や画像 URL などの変数値埋め込みをプログラムしていくことに集中できます。

Flexible Template Simulator

Flexible Template Simulator を開くとこんな感じで既にサンプルが表示されてる状態から始まります。

image-01.png

左上の「サンプル」ボタンをクリックすると、さらにバリエーション豊かなサンプルが選べます。今回のボットでは「Brochure」をベースにしました。

image-02.png

「作成」をクリックすると編集モードに移行します。色々矢印を引いた通り、1つ1つ部品ツリーになっているので見ながら調整します。この辺りは CSS の基礎知識があれば十分にカスタマイズできる感じでした。

image-03.png

仕上げにアクションを受け付ける部分の Box を選んで、右下にある「Action」で色々定義します。値部分は埋め込む想定に近いダミー値を配置しておきます。後で巨大な JSON から該当部分を探すのに役立ちます。

今回は「投票する!」ボタンとして作り込みます。押したらボットへ、にゃんこIDを伴ってコールバック受信してほしいので postback タイプで設定しました。

image-04.png

あとは右上の「JSONで表示」をクリックして目的の雛形を手に入れます。

image-05.png

どうやって使うか

に書いてある通りではありますが、エンドポイントに POST するペイロード部分を以下のようにします。

{
  "type": "flex",  // お決まり
  "altText": "にゃんこ投票",
  "contents": {
    // ★ここに Flexible Template Simulator で生成した JSON を張り付ける
  }
}

後ほどプログラムするときのために JSON ファイルなどにサンプルとして保存しておくと良いです。Flexible Template Simulator に保存機能は無いのでブラウザーを閉じると全て消えます。

余談ですが、ここでかなりハマりました。contents の位置が分からなかったです。
最初はコピーした内容をまるごとメッセージ送信のペイロードになるのかと勘違いして送信したら HTTP 400 応答で失敗しました。

そろそろ猫成分を補充しましょう!

🐈 Cats as a Service !

「猫画像 API」でググったら見つけました。サービスとしてのにゃんこたち!

the-cats-api.png

ロゴマークのキュートさにやられましたわ。

未登録だと 1 画像のみ検索・指定なし検索(10件)しかできず、絞り込みや投票もできないので、アカウント登録して API キーを発行しました。

無料登録と API 仕様

下部にある「SIGNUP FOR FREE」からできます。登録したメールアドレス宛に API キーが送られてくるので保管しておきます。

細かな仕様については右上の「DOCUMENTATION」から見れます。OpenAPI 形式の YAML の手に入るので使い勝手はかなり良いです。

🐈🐈 ネコる

今回はバックエンドの API サーバーとして nestjs をフレームワークにして起動しつつ、ngrok で Webhook 受付用の URL をこさえる事にしました。この構成だとクラウド等にデプロイする必要もなく、クラウドサービスと連携できるのですごく便利です。

nestjs.png

image.png

nestjs をデフォルト設定で起動すると「 http://locahost:3000 」でアクセスできるので、それにあわせて「ngrok http 3000」としてトンネルさせる算段です。

チャットを送る時はローカルの nestjs から直接 LINE WORKS の API にアクセスします。

実装

かなりボリュームがあるので主要どころのみ。ソースコードは追って github に公開します。

app.controller.ts

HTTP エンドポイント部分の作り。
http://localhost/webhooks/適当なID で受け付けるようにしました。

image.png

本来は X-WORKS-Signature を使った HMAC-SHA256 によるメッセージ検証をすべきですが、書き方が悪いらしくシグネチャが一致せず。
ここは今後の課題になりました。

app.service.ts

まずは Webhook コールバックを受け付けて、該当キーワードがあったら 5 匹のにゃんこ画像を送ったり、投票の処理&おかわり送信をしています。

image.png

Flexible Template Simulator で生成した JSON をもとに繰り返しや値埋め込みを行っていきます。これだけで 120 行に達するので別ファイルにするかテンプレートシステム的なものを入れると良いかも。

image.png

lineworks.service.ts

続いて LINE WORKS 関連を nestjs サービス化します。
API 2.0 に関する認証情報・秘密鍵などは環境変数(.env ファイル)から収集するようにしました。この実装のベースは自分の記事 LINE WORKS API 2.0 でスタンプメッセージを送ろう - 実装編 を読み返しつつ組み込みました。

image.png

the-cats.service.ts

癒しの供給元 The Cats API アクセスについても nestjs サービス化していきます。
LINE WORKS 同様に API キーを環境変数から受け取ります。

image.png

サーバー起動と公開

ngrok

先に ngrok を立てます。

この記事が参考になりました。感謝!

ngrok http 3000

image.png

Web アクセスできる URL が発行されるのでメモします。

LINE WORKS ボットのコールバック設定

API 2.0 アプリの作成とボットの登録は済ませ、「Callback URL」にメモした URL を入れつつアプリ実装のパスも付け足しておきます。

image.png

.env

環境変数の設定を済ませます。
実装の this.config.getOrThrow<string>("LINEWORKS_CLIENT_ID") などのようにして読み取ることができます。

.env
THE_CAT_API_KEY = "live_xxxxxxxxxxxxxxxxxxxxxxxxxxxx"

LINEWORKS_BOT_ID = "1234567"

LINEWORKS_CLIENT_ID = "xxxxxxxxxxxxxxxxxxxx"
LINEWORKS_CLIENT_SECRET = "xxxxxxxxxx"
LINEWORKS_SERVER_ACCOUNT = "xxxxx.serviceaccount@xxxxxxxx"
LINEWORKS_PRIVATE_KEY = "
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCFTUyFCZOe+i3m
省略
Gr4AbGF41v7O8Cnwfnnq23Y=
-----END PRIVATE KEY-----
"

nestjs 起動

npm run start:dev

image.png

これで連結完了です。

この構成の注意点

ngrok は起動するごとに払い出されるアドレスが変わるので、起動の都度 LINE WORKS のボット設定にあるコールバック URL のメンテが必要です。そこは割り切りとしました。

おわりに

なんとか滑り込みで今日のうちにだせたw(早くやっとけ
思いのほか、にゃんこ選択が面白くてデバッグと称してクリックしまくっていたのが時間泥棒になってしまった・・。

アドベントカレンダー参加をきっかけに色々なサービスや機能に触れられたのは新鮮で良かったです。

それではまた!

8
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?