TypeScriptでMessagingAPIを介してLINEBotのリッチメニューを設定する方法

# はじめに
TypeScriptを使ってLINEBotの開発をしている中,リッチメニューの更新が[公式リファレンス](https://developers.line.biz/ja/docs/messaging-api/using-rich-menus/)で解説されている方法だとめちゃめんどくさいと思いました．
そのため,実行するだけで一発でリッチメニューの更新をできるようなコードを実装しました．
TypeScriptとして探すとあまり記事がみつからので共有しておこうと思います．

# 環境
- node.js 16.19.0
- line/bot-sdk 7.5.2

# 実装
とりあえず動かしたい人は，下記コードをコピーしてそのファイルを実行すれば動くと思います．その時にConfig.tsの作成を忘れないでください．
解説はコードの後に載せておきます．
```updateRichMenu.ts
import * as fs from 'fs';
import { Client,RichMenu } from '@line/bot-sdk';
import { CHANNEL_ACCESS_TOKEN, RICH_MENU , IMAGE_PATH} from "./config";

class RichMenuCreater {

  #client: Client
  #richmenu: RichMenu
  #richMenuId: string = ""

  constructor(client: Client, richmenu: RichMenu) {
    this.#client = client;
    this.#richmenu = richmenu;
  }

  // デフォルトのリッチメニューを適用する
  async applyDefaultRichMenu() {
    await this.#createRichMenu();
    await this.#setRichMenuImage();
    await this.#setDefaultRichMenu();
  }

  // リッチメニューを作成する
  async #createRichMenu() {
    await this.#client.createRichMenu(this.#richmenu).then((richMenuId) => {
      this.#richMenuId = richMenuId;
    });
  }

  // リッチメニューに画像をアップロードして添付する
  async #setRichMenuImage() {
    await this.#client.setRichMenuImage(
      this.#richMenuId,
      fs.createReadStream(IMAGE_PATH)
    );
  }

  // デフォルトのリッチメニューを設定する
  async #setDefaultRichMenu() {
    await this.#client.setDefaultRichMenu(this.#richMenuId);
  }
}

const client = new Client({
  channelAccessToken: CHANNEL_ACCESS_TOKEN,
});
const richmenu = RICH_MENU;

const richMenuCreater = new RichMenuCreater(client, richmenu);

// リッチメニューを作成し，デフォルトに適用する
richMenuCreater.applyDefaultRichMenu();
```

上から順に解説していきます．

### import
```ts
import * as fs from 'fs';
import { Client,RichMenu } from '@line/bot-sdk';
import { CHANNEL_ACCESS_TOKEN, RICH_MENU , IMAGE_PATH} from "./config";
```
画像の読み込みのためにfs,リッチメニューの各種実装のためにline/bot-sdkからClientとRichMenu,各種定数のためにconfigから読み込んでいます．

### メンバ変数
```ts
  #client: Client
  #richmenu: RichMenu
  #richMenuId: string = ""
```
RichMenuCreaterのメンバ変数ですね．
Node.jsの場合は書かなくても動くようなのですが，今回はTypeScriptなので明示的に用意する必要があります．
自身が持つ公式アカウントとやりとりするためのclient.
設定するリッチメニューを持つrichmenu.
そして，作成したリッチメニューを指定するためのrichMenuIdの計三つです．

変数名の前についてる#はなんぞやとなるなる人も多いでしょう．僕はなりました．
この#はnode.js由来の仕様のようで，接頭辞に#をつけて命名すると変数や関数が**プライベートフィールドになる**ようです．
つまり，#をつけることで変数や関数の呼び出し範囲をクラス内だけに限定できるようになります．private修飾子と似たような感じでしょうか．

### コンストラクタ
```ts
constructor(client: Client, richmenu: RichMenu) {
  this.#client = client;
  this.#richmenu = richmenu;
}
```
クラスのインスタンス化をする際の処理です．
アカウントと連携するclientと表示したいrichmenuを受けとってメンバ変数に保存しています．

### applyDefaultRichMenu関数

```ts
async applyDefaultRichMenu() {
  await this.#createRichMenu();
  await this.#setRichMenuImage();
  await this.#setDefaultRichMenu();
}
```
外部から呼び出せる唯一の関数です．
これを呼び出すだけでリッチメニューの作成，画像のセット，デフォルト表示への設定が済むようになっています．

### #createRichMenu関数
```ts
async #createRichMenu() {
  await this.#client.createRichMenu(this.#richmenu).then((richMenuId) => {
    this.#richMenuId = richMenuId;
  });
}
```
リッチメニューの作成を行う関数です．
公式リファレンスの[リッチメニューの作成](https://developers.line.biz/ja/docs/messaging-api/using-rich-menus/#create-a-rich-menu)を担っています．
linesdk側で用意されているcreateRichMenuにrichmenuを渡すことで，リッチメニューの作成を行っています．
無事にリッチメニューの作成が完了するとそのリッチメニューを示すrichMenuIdが返ってくるのでそれをメンバ変数に保存します．


### #setRichMenuImage関数
```ts
async #setRichMenuImage() {
  await this.#client.setRichMenuImage(
    this.#richMenuId,
    fs.createReadStream(IMAGE_PATH)
  );
}
```
作成したリッチメニューへの画像アップロードを行なっている関数です．
公式リファレンスの[リッチメニューに画像をアップロードして添付する](https://developers.line.biz/ja/docs/messaging-api/using-rich-menus/#upload-the-rich-menu-image)を担っています．
先ほど受け取ったrichmenuIdを使ってfsで読み込んだ画像をリッチメニューにセットしています．

### #setDefaultRichMenu関数
```ts
async #setDefaultRichMenu() {
  await this.#client.setDefaultRichMenu(this.#richMenuId);
}
```
作成し，画像を設定したリッチメニューをアカウントのデフォルトへと設定する関数です．
公式リファレンスの[デフォルトのリッチメニューを設定する](https://developers.line.biz/ja/docs/messaging-api/using-rich-menus/#set-the-default-rich-menu)を担っています．
richmenuIdを使用して，アカウントがデフォルトで表示するリッチメニューに先ほど作成したリッチメニューを設定しています．

### リッチメニュー適用の実行 
```ts
const client = new Client({
  channelAccessToken: CHANNEL_ACCESS_TOKEN,
});
const richmenu = RICH_MENU;

const richMenuCreater = new RichMenuCreater(client, richmenu);

richMenuCreater.applyDefaultRichMenu();
```

# おわりに
以上が**TypeScriptでMessagingAPIを介してLINEBotのリッチメニューを設定する方法**です．
参考記事に載っているものを大いに参考にさせていただきTypeScript向けに改修して．個人的な解釈を記事にしました．
私自身LINEBot開発を始めてまだ2ヶ月程度しか経っていないため，それらに関する理解が十分とはいえません．そのため，もし「それはちがうよ！」という部分があれば指摘してもらえると幸いです．


# 参考記事
https://www.wantedly.com/companies/company_7005547/post_articles/409034