🕌

Cloud Functions × TypeScript の環境でMessagingAPIのImageMap Messageを実装する

に公開

研究でLINEBotを開発していく中で,イメージマップメッセージの実装をする際にはまった部分が何個かあったのでその共有をします.
あとシンプルに情報が少なすぎて悲しいので自分で増やすこととします.
もし,途中の表現や解釈で「それは違うよ!」というのがあったら教えてください

環境

@line/bot-sdk: 7.5.2
express: 4.18.2
typescript: 4.9.0

前提

CloudFunction + TypeScriptの構成で,既にLINEBotが動作していることを前提としています.そのため,LINEBotそのものの開発やデプロイについては触れません.

この記事ですること

  • LINEMessagingAPIを使ったwbehook側のImageMap Message周りの実装
  • Cloud Functionsを利用してImageMap Message用の画像を公開する方法

イメージマップメッセージとは

ImageMap Message?何それおいしいの?という人向けに軽く紹介します.ちなみにドキュメントに書いてある情報以上のことは一切ないので,スルーしてもらっても構いません.
https://developers.line.biz/ja/docs/messaging-api/message-types/#imagemap-messages
イメージマップメッセージはLINEで利用できるメッセージのうちの一つで,タップやアクションが可能なめちゃでか画像や動画が送信できるというものです.
公式アカウントでは,広告などユーザへ強くアプローチしたい場合に使われているイメージです(独断と偏見).

ImageMap Messageはただ画像を用意すればいいわけではなく,Messaging APIで指定された形のURLでその画像が取得できるようにする必要があります.これがめどかった

指定された形のURLは以下のような条件です.

baseUrl/{image width}」というURL形式で、5つの異なるサイズの画像にアクセスできるようにしてください。LINEはユーザーのデバイスに応じて、適切な解像度の画像を取得します。
画像のURLには拡張子を含めないでください。

それに加えてImageMap Messageでは,横幅が1040,700,460,300,240pxの計5つの異なるサイズの画像をそれぞれ用意し,それぞれにアクセスができるようにする必要があります.
つまり,1つのImageMap Messageを利用する場合でも,5種類の画像を用意してそれぞれを公開する必要があるということです.
https://developers.line.biz/ja/reference/messaging-api/#imagemap-message

ImageMap Messageで利用する画像をCloud Functionsでデプロイする

では,早速前述した「指定された形のURL」で画像を取得できるようにしましょう.

ディレクトリ構造

まずはディレクトリ構造からです.

functions
├ lib     // TypeScriptをコンパイルしたJavaScriptのコード(deply時に自動生成) 
├ src     // TypeScriptのコード
└ images
  └ imageMapImage // イメージマップ用の画像を格納
    ├ 1040        // それぞれのディレクトリ名に合わせたサイズ画像を入れる
    ├ 700
    ├ 460
    ├ 300
    └ 240

imagesディレクトリはfunctionsの直下,正確にはsrcの外側に置く必要があります.
functionsでデプロイされるコードはsrcではなく,srcをコンパイルしてJavaScriptにしたlibです.(正確な表現ではないかもしれません)
そのため,srcの内部にimagesを置いてしまうと,参照ができずに無が帰ってくるようになってしまいます.(1敗)

TypeScriptによる実装

次に実装です.

index.ts
import * as functions from "firebase-functions";
import * as express from "express";
import * as fs from "fs";

const lineImagesApp = express();

lineImagesApp.get("/:imageName/:size", (req, res) => {
  const imageName = req.params.imageName + ".png";
  fs.readFile("./images/imageMapImage/" + req.params.size + "/" + imageName, (err, data) => {
    if (err) {
      res.status(404).json({
        message: err.message,
        status_code: res.statusCode,
        path: `${req.method}:${req.originalUrl}`,
      });
    } else {
      res.type("png");
      res.send(data);
      res.status(200);
    }
  });
});

export const lineImages = functions.region("asia-northeast1").https.onRequest(lineImagesApp);

大事そうなところを1つずつ解説していきます.

パスパラメータの設定・取得

lineImagesApp.get("/:imageName/:size", (req, res) => {
  const imageName = req.params.imageName + ".png";

パスに記述している:imageName:sizeがパスパラメータです.
パスのその部分に該当する文字列をパラメータとして取得することができます.
FirebaseUrl/lineImages/hoge/1040というURLだった場合,hogeがimageName,1040がsizeになります.
req.params.{パスパラメータ}で任意のパスパラメータを取得できます.

画像の読み込み

fs.readFile("./images/" + req.params.imageName + "/" + req.params.size + "/" + imageName, (err, data) => {
  ...
  res.type("png");
  res.send(data);
  ...
}

readFileの第一引数に前述したディレクトリ構造に合うようにパスを渡します.パスパラメータを利用することで,複数のイメージマップメッセージでも1つのAPIで呼び出せるようになっています.
res.type("png");で画像の拡張子を指定します.もしJPG(JPEG)を利用したい場合はここをres.type("jpeg");にしてください.

ここまで来たらあとはデプロイするだけでImageMap Messageからアクセスする準備が整いました.
デプロイした後にちゃんと表示されるか全部のサイズのURLを確認してみることをおすすめします.

ImageMap Messageを実装する

それではついにLINEBotからImageMap Messageを送信できるようにしましょう.
今回は一番シンプルな,1枚の画像でタップ可能なものを例としてあげます.
もしもっと複雑なMessageを実装したい場合はリファレンスのJSON形式を参考に色々とこねこねしてみてください.

ImageMapMessage.ts
const testImageMapMessage: ImageMapMessage = {
  "type": "imagemap",
  "baseUrl": "{Cloud FunctionsURL}/lineImages/{画像名}",
  "altText": "イメージマップメッセージ",
  "baseSize": {
    "width": 1040,
    "height": 520,    //画像の高さに合わせて調整
  },
  "actions": [
    {
      "type": "message",
      "label": "タップ",
      "text": "タップしました",
      "area": {
        "x": 0,
        "y": 0,
        "width": 1040,
        "height": 520,  //画像の高さに合わせて調整
      },
    },
  ],
}

上記のtestImageMapMessageclient.replyMessageに渡せば,ImageMap Messageが送信されるはずです.

おわりに

これで「Cloud Functions × TypeScript の環境でMessagingAPIのImageMap Messageを実装する」は以上です.
「ImageMap Message実装したいけどわっかんね〜」という奇特な人がいるかはわかりませんが役に立てたなら何よりです.
ちなみもし「もっとこの方がいい」とか「ここが違う」とかあったら気軽に言ってもらえると喜びます.

参考にした記事

Discussion