NodeでLINE Botを扱うには

この記事はNode.jsアドベントカレンダーの13日目の記事です。

どうも！ハムカツおじさんという名前でtwitterやってます([@hmktsu](https://twitter.com/hmktsu))🤘
自分でだったり弊社でだったりなどNode.js(TypeScript)を使ってサービスを作っています。

## はじめに

現在とあるデータを毎回取得して、その中に特定の文字列ならびに新規データが存在しているならば、LINE Botを使って自分に通知をするというツールを作って運用をしています。

とある商品群のデータがリアルタイムに欲しいので作りました。
Slackを使って通知するという形でも問題はないのですが、エンジニアだけがそのサービスを使いたいわけではない、ましてや普通の人でもという考えがあるので、LINE Botを使うことにしました。

ということで今回はLINE Botを使って通知を自分に送ってみようという内容を書きたいと思います。
なお**LINE Botを使うための登録云々については割愛**させていただきます🙇‍♂️

## LINE Botを無料で使いたい
基本的に開発および少人数であるならば無料でいけると思います。
1000通がフリープランの目処になります。

[プラン・料金](https://at.line.me/jp/plan)
[LINE Official Account Manager](https://manager.line.biz/)

両方で書かれてることが若干違います。(ターゲットリーチ数×吹き出し数1,000通までなど)
正直自分はどんなに頑張っても1000通は送らないし、吹き出し数計算でも1000通はいかないので今のところ無料で使えています。
1000通送るかどうか、これを1つの指標として考えるといいのではないかと思います。

## インストール
では本題のLINE Botで通知を送ってみるために、とりあえずまずはインストールしましょう。

```
$ npm install --save @line/bot-sdk
```

## アクセストークンの取得
次は送信するためにアクセストークンを取得する必要があります。

```typescript
type Token = {
  access_token: string,
  expires_in: number,
  token_type: string,
}

type TokenError = {
  error: string,
  error_description: string,
}

const getAccessToken = (): Promise<Token | TokenError> => request({
  url: 'https://api.line.me/v2/oauth/accessToken',
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
  },
  form: {
    grant_type: 'client_credentials',
    client_id: process.env.CHANNEL_ID,
    client_secret: process.env.CHANNEL_SECRET,
  },
}).then(body => JSON.parse(body)).catch(body => JSON.parse(body.error));
```

このような関数を作ってあげると簡単です。
`client_id`と`client_secret`については`process.env`で管理すると楽です。

```typescript
const setAccessToken = (token: string = null): line.Client => {
  const config: line.ClientConfig = {
    channelAccessToken: token,
    channelSecret: process.env.CHANNEL_SECRET,
  };

  const client = new line.Client(config)

  return client;
}
```

送信する前にアクセストークンは取得する必要があるので、このようにアクセストークンをセットしたら`line.Client`を返してくれるような関数も用意しましょう。

## 1人に送るか？まとめて送るか？
1人に送る場合は`push`を、友達になっている人全員に送るには`broadcast`を使います。

```typescript
const push = async (
  to: string = null,
  messages: line.Message[],
): Promise<void> => {
  const token = await getAccessToken();

  if ('error' in token) {
    console.warn(`[line push] ${token.error}`);
    return;
  }

  if (!token.access_token) {
    console.warn('[line push] token is null');
    return;
  }

  const client = setAccessToken(token.access_token);

  await client.pushMessage(to, messages);
}

const broadcast = async (
  messages: line.Message[],
): Promise<void> => {
  const token = await getAccessToken();

  if ('error' in token) {
    console.warn(`[line push] ${token.error}`);
    return;
  }

  if (!token.access_token) {
    console.warn('[line push] token is null');
    return;
  }

  const client = setAccessToken(token.access_token);

  await client.broadcast(messages);
}
```

基本的に送り方は`push`も`broadcast`も変わりません。

## 送ることができる内容

[Messaging API overview](https://developers.line.biz/en/docs/messaging-api/overview/)

`push`も`broadcast`も複数のメッセージ形式に対応しています。
プレーンなテキストもビデオもスタンプも色々と対応していますが、自分がよく使うのはTemplate Messageの`Carousel`と`Image carousel`です。
目的に応じて使い分けるとよいと思います。

![Carousel](https://developers.line.biz/media/messaging-api/messages/carousel-c59baef6.png)

```json
{
  "type": "template",
  "altText": "this is a carousel template",
  "template": {
      "type": "carousel",
      "columns": [
          {
            "thumbnailImageUrl": "https://example.com/bot/images/item1.jpg",
            "imageBackgroundColor": "#FFFFFF",
            "title": "this is menu",
            "text": "description",
            "defaultAction": {
                "type": "uri",
                "label": "View detail",
                "uri": "http://example.com/page/123"
            },
            "actions": [
                {
                    "type": "postback",
                    "label": "Buy",
                    "data": "action=buy&itemid=111"
                },
                {
                    "type": "postback",
                    "label": "Add to cart",
                    "data": "action=add&itemid=111"
                },
                {
                    "type": "uri",
                    "label": "View detail",
                    "uri": "http://example.com/page/111"
                }
            ]
          },
          {
            "thumbnailImageUrl": "https://example.com/bot/images/item2.jpg",
            "imageBackgroundColor": "#000000",
            "title": "this is menu",
            "text": "description",
            "defaultAction": {
                "type": "uri",
                "label": "View detail",
                "uri": "http://example.com/page/222"
            },
            "actions": [
                {
                    "type": "postback",
                    "label": "Buy",
                    "data": "action=buy&itemid=222"
                },
                {
                    "type": "postback",
                    "label": "Add to cart",
                    "data": "action=add&itemid=222"
                },
                {
                    "type": "uri",
                    "label": "View detail",
                    "uri": "http://example.com/page/222"
                }
            ]
          }
      ],
      "imageAspectRatio": "rectangle",
      "imageSize": "cover"
  }
}
```

![Image Carousel](https://developers.line.biz/media/messaging-api/messages/image-carousel-301701f6.png)

```json
{
  "type": "template",
  "altText": "this is a image carousel template",
  "template": {
      "type": "image_carousel",
      "columns": [
          {
            "imageUrl": "https://example.com/bot/images/item1.jpg",
            "action": {
              "type": "postback",
              "label": "Buy",
              "data": "action=buy&itemid=111"
            }
          },
          {
            "imageUrl": "https://example.com/bot/images/item2.jpg",
            "action": {
              "type": "message",
              "label": "Yes",
              "text": "yes"
            }
          },
          {
            "imageUrl": "https://example.com/bot/images/item3.jpg",
            "action": {
              "type": "uri",
              "label": "View detail",
              "uri": "http://example.com/page/222"
            }
          }
      ]
  }
}
```

## まとめ
簡単にですが、LINE Bot + Node.js(TypeScript)でLINE Botからメッセージを送るにはということを説明させていただきました。

何よりも自分が身近に使っているアプリに対して通知を送ることができるのがとても便利なのでおすすめです。
開発用途で使う分にはほぼ無料でいけるはずなので、何か簡単なものを作るのに最適だと思います