Node.jsとMessagingAPIでLINEの応答ボットを作る

## はじめに
業務でLINEのMessagingAPIを使ったサービスの開発をすることになりました。
練習のためLINEの応答ボットを作ってみたので、その手順を説明します。
なお、本記事は以下2点が前提となっています。
* JavaScript、Node.jsの知識があること
* GitHubのアカウントを持っていること

## LINE関連の設定
### LINE Business IDの作成
Messaging APIの機能を使用するためにはLINE Business IDが必要です。
以下のURLにアクセスします。
https://manager.line.biz/

「アカウントを作成」をクリックします。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/5f6c4dbf-5f49-fcc1-640c-ab668be837a0.png'>

「メールアドレスで登録」を押下します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/abfbd30a-0cd4-dcbc-bf01-c235d09dc99e.png'>

使用するメールアドレスを入力し、「登録用のリンクを送信」を押下してください。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/c58e2fe7-4890-4653-18ad-74e0637bdd53.png'>

入力したメールアドレス宛に、以下のようなメールが届きます。
「登録画面に進む」を押下します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/6c2243bb-e19d-8fee-940f-709d95e4c19d.png'>

名前とパスワードを入力し、「登録」を押下します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/c65b5082-7308-6c17-9317-ff686379f07d.png'>

登録内容を確認し、問題なければ「登録」を押下します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/3b1a22ec-26a6-fcdc-24cd-ead770320401.png'>

これでBusiness IDの登録は完了です。
「サービスに移動」を押下します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/2a85fca3-0587-573c-0980-0439273165b1.png'>

### 開発者アカウントの登録
LINE Business IDを登録した後、初回ログイン時のみ開発者アカウントの登録を求められます。
開発者名、メールアドレスを入力し、同意のチェックを入れて「アカウントを作成」を押下してください。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/67df7e95-8a5f-be14-1189-be6f12ce4bbf.png'>

開発者アカウントが登録できました。

### プロバイダーの作成
画面下部の「新規プロバイダー作成」を押下します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/21ae91f5-b0dd-766c-5c5f-ea757c2b8724.png'>

任意のプロバイダー名を入力し、「作成」を押下します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/364bcc53-eb74-bbe5-5723-74e1d96dd154.png'>

### チャネルの作成
チャネルはLINEが提供する機能をプロバイダーが使うための窓口になります。
また、チャネルを作成すると、チャネルと同名の公式アカウントも作成されます。
「Messaging API」をクリックしてください。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/25f0fe4e-21af-b755-9c33-8e4b83f58d4a.png'>

それぞれ必要な項目を入力します。任意項目については入力は不要です。
一通り入力できたら「作成」を押下します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/b1c9125d-fc86-6a79-0316-ed37db40d888.png'>

内容を確認し、問題なければ「OK」を押下します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/82d41fe9-3d84-059a-ef89-5fbd634b9ad2.png'>

権利利用と情報提供について同意を求められるので同意しておきます。
（いずれも規約を最後までスクロールしないと「同意する」が活性化しません。）
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/e971d104-7d2d-6bf2-d458-a6ee13bed5c1.png'>
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/f2278308-faae-ed59-a7a9-6c01d3c52d38.png'>

以上でチャネルの作成は完了です。

後ほどMessaginAPIの設定を行います。

## Renderでボットをホストするサーバを作成
Renderは簡単に静的サイトやWebサービスなどをデプロイできるサービスです。
最低限のものであれば無料で利用することができるので、ちょっと試すのに適しています。
ボット作成のためにはサーバが必要なので、今回はこちらを使っていきます。

### リポジトリをフォークする
Renderではサービスを簡単に作成することができるよう、様々な言語や環境に対応したテンプレートをGitHubに用意しています。
今回はNode.jsのフレームワークであるExpressを使用して開発をするため、Express用のリポジトリを使用します。

以下のリポジトリをフォークします。
https://github.com/render-examples/express-hello-world

### アカウントを作成する
以下のURLからアカウントを作ります。
https://dashboard.render.com/register

メールアドレスのほか、GitHub、GitLab、Googleのアカウントと連携できます。
私はGoogleのアカウントと連携させました。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/967ac394-1495-0fe4-4885-c5a7abc30714.png'>

アカウントの作成が完了すると、以下のようなダッシュボードの画面に遷移します。

新しくWebサービスを作りたいので「New Web Service」を押下します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/36008dae-e2e9-26ec-9499-334eb4d44bb3.png'>

初期状態（Build and deploy from a Git repository）のままで「Next」を押下します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/1b33342a-cf78-a926-72c7-4f2a073418b3.png'>

GitHubの「Connect account」を押下します。 
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/61f2a1f8-a7a6-3bd9-58a6-8ef50ab79fe6.png'>

GitHubアカウントの連携を済ませた後、Renderをどのリポジトリにインストールするかを聞かれるので、「Only select repositories」を選択し、冒頭でフォークしたリポジトリを選びます。
画面下部の「Install」を押下します。
<img width='100%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/c38926f7-f594-0a54-c5ef-10d341dd0977.png'>

Renderのサイトに戻ってくると、先ほど選択したリポジトリが表示されているので、「Connect」を押下します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/5191ee23-cb57-2792-3312-3d26d959b9dd.png'>

Webサービスを作成するための設定を入力します。
変更する箇所は2箇所、あとは初期状態のままでよいです。
・Name
任意の名前。ここで指定した名前が、WebサービスにアクセスするためのURLに使われる。

・Instance Type
Freeを選択。無料で使える。クレジットカード情報の入力も不要

入力が完了したら、画面下部の「Create Web Service」を押下
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/37fc35b0-1dc7-d1cb-6de2-f177f5f3ab45.png'>

Webサービスのデプロイが始まります。しばらくは「Building」となっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/1775b412-aea6-3e38-e204-cd50c87b9a3e.png)

ビルドが終わると、「Live」に変わります。これでWebサービスのデプロイが完了です。
左上にある「https://~」のリンクを押下してください。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/e96d2f5c-3d20-84ad-9e13-bbddcd0ad22b.png'>

「Hello from Render!」が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/b5778461-cdcd-b332-1b7d-c2f731cebce6.png)

このようにして、ほんの数クリックで簡易なWebサービスのデプロイをできるのがRenderのいいところです。

## 応答ボットの処理を実装
アカウントの作成、サーバの作成が完了したので、実際に処理を実装していきます。

### JavaScriptで処理を書く
処理を記述するため、まずは自分のGitHubアカウントにフォークしてきたリポジトリを、ローカルで編集できるよう、任意のフォルダにクローンしてください。

以下のようにいくつかファイルがありますが、このうちapp.jsを編集していきます。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/86e61142-981f-7e7d-6865-7d3717094c1c.png'>

既存の記載はすべて消して、ボットの処理を実装します。
#### モジュールのインポート
```javascript
const https = require("https");
const express = require("express");
```
httpsモジュール、expressモジュールを使用します。

#### 環境変数の取得
```javascript
// ポート番号
const PORT = process.env.PORT || 3000;
// Messaging APIを呼び出すためのトークン
const TOKEN = process.env.LINE_ACCESS_TOKEN;
```
ポート番号とトークンを取得します。
ポート番号は未設定の場合には3000が割り当てられるようにしています。
トークンについてはRender側で後ほど環境変数を設定します。

#### Expressアプリケーションオブジェクトの生成
```javascript
const app = express();
```
ミドルウェアやルーティングの設定を行うために使用します。

#### ミドルウェアの設定
```javascript
app.use(express.json());
app.use(express.urlencoded({ extended: true, }));
```
use関数を使い、リクエストとレスポンスの間で行われる処理を定義します。
今回はJSONデータとフォームデータを扱えるよう設定しています。
（フォームデータは今回使いませんが、この2つを設定するのが一般的なようです。）

#### ルーティングの設定-ドメインのルート
```javascript
app.get("/", (_, res) => {
  res.sendStatus(200);
});
```
URLの検証の失敗などを防ぐため、ドメインのルート(`/`)へのHTTP GETリクエストに対して、ステータス200を返すようにします。

#### ルーティングの設定-MessaginAPI
```javascript
app.post("/webhook", (req, _) => {
  res.send("HTTP POST request sent to the webhook URL!");
  if (req.body.events[0].type === "message") {
    const headers = {
      "Content-Type": "application/json",
      Authorization: "Bearer " + TOKEN,
    };
    const dataString = JSON.stringify({
      replyToken: req.body.events[0].replyToken,
      messages: [
        {
          type: "text",
          text: "Hello, user",
        },
        {
          type: "text",
          text: "May I help you?"
        },
      ],
    });
    const webhookOptions = {
      hostname: "api.line.me",
      path: "/v2/bot/message/reply",
      method: "POST",
      headers: headers,
      body: dataString,
    }
    const request = https.request(webhookOptions, res => {
      res.on("data", d => {
        process.stdout.write(d);
      });
    });
    request.on("error", err => {
      console.error(err);
    });

    request.write(dataString);
    request.end();
  }
});
```
Messaging APIでは`/webhook`に対してHTTP POSTリクエストが送られるため、それに対しての処理を実装します。
##### `res.send(...`
httpレスポンスを返す処理です。
今回は「"HTTP POST request sent to the webhook URL!"」という文字列をレスポンスボディに設定しています。

##### `if (req.body.events...`
ユーザがボットにメッセージを送ったときに処理をするよう、分岐を入れています。
以降の処理はすべてif文のブロック内に記述します。

##### headers
MessagingAPIの応答メッセージに必要はリクエストヘッダを設定しています。
`Content-Type`は`application/json`で固定です。
`Authorization`は`Bear `にMessagingAPIを呼び出すためのトークンを設定します。

##### dataString
MessagingAPIの応答メッセージに必要なリクエストボディを設定しています。
`replyToken`はWebhookイベントオブジェクトに含まれています。
`messages`はユーザーに送信されるメッセージです。オブジェクトで受け渡します。
今回はテキストタイプのメッセージオブジェクトを設定しています。

##### webhookOptions
httpsモジュールのrequest関数に必要な情報をオブジェクトとして設定しています。
`hostname`はMessagingAPIのエンドポイントのドメイン名である`api.line.me`を設定しています。
`path`は応答メッセージのAPIのエンドポイントである`/v2/bot/message/reply`を設定しています。
`method`は`POST`です。
`headers`、`body`にはそれぞれ先ほど設定したリクエストヘッダ情報である`headers`、リクエストボディ情報である`dataString`を設定しています。

##### `const request = https.request...`から`request.end()`まで
httpsリクエストを生成し、エラー時の挙動を設定しています。
また、httopsリクエストの生成だけではリクエストボディが確定していないので、write関数に再度リクエストボディを設定しています。
最後のend関数でリクエストを送信して`/webhook`に対する処理は完了です。

#### リスナーの設定
```javascript
app.listen(PORT, () => {
  console.log(`Example app listening at http://localhost:${PORT}`);
});
```
最後に冒頭で取得したポート番号をリッスンします。
これですべての実装が完了しました。

<details><summary>ソースコード全文を表示</summary>

```javascript 
// モジュールのインポート
const https = require("https");
const express = require("express");

// 環境変数の取得
// ポート番号
const PORT = process.env.PORT || 3000;
// Messaging APIを呼び出すためのトークン
const TOKEN = process.env.LINE_ACCESS_TOKEN;

// Expressアプリケーションオブジェクトの生成
const app = express();

// ミドルウェアの設定
app.use(express.json());
app.use(express.urlencoded({ extended: true, }));

// ルーティングの設定-ドメインのルート
app.get("/", (_, res) => {
  res.sendStatus(200);
});

//ルーティングの設定-MessaginAPI
app.post("/webhook", (req, _) => {
  if (req.body.events[0].type === "message") {
    res.send("HTTP POST request sent to the webhook URL!");
    const headers = {
      "Content-Type": "application/json",
      Authorization: "Bearer " + TOKEN,
    };
    const dataString = JSON.stringify({
      replyToken: req.body.events[0].replyToken,
      messages: [
        {
          type: "text",
          text: "Hello, user",
        },
        {
          type: "text",
          text: "May I help you?"
        },
      ],
    });
    const webhookOptions = {
      hostname: "api.line.me",
      path: "/v2/bot/message/reply",
      method: "POST",
      headers: headers,
      body: dataString,
    }
    const request = https.request(webhookOptions, res => {
      res.on("data", d => {
        process.stdout.write(d);
      });
    });
    request.on("error", err => {
      console.error(err);
    });

    request.write(dataString);
    request.end();
  }
});

// リスナーの設定
app.listen(PORT, () => {
  console.log(`Example app listening at http://localhost:${PORT}`);
});
```
</details>


ここまでの内容をコミット、プッシュします。
GitHubにプッシュすると、Renderにデプロイしたサービスが、自動で再度デプロイされます。

デプロイ完了後、Renderで作成したWebサービスへアクセスすると、先ほどの「Hello from Render」ではなく「ok」だけが表示されていることが確認できます。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/cf725e7a-74e4-ad50-8c42-fc755322d73e.png'>

## MessagingAPI関連の設定
### Webhook設定
まずは作成したチャネルに、デプロイしたサービスを紐づけます。
チャネルの画面から「Messaging API」タブをクリックします。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/1aaf42d0-d2c6-5aea-3f0c-30f159ce369d.png'>

Webhook URLの「編集」を押下します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/f3f95442-9ccf-ebda-253d-71d3bef41536.png'>

RenderでデプロイしたサービスのURLの末尾に「/webhook」付け加えたものを入力し、「更新」を押下します。
※末尾に「/webhook」がないと正しく動きません
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/c38a27af-ba25-9edd-affc-923455842964.png'>

「検証」を押下します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/cb47e503-2eec-8ae2-b28c-a83d4878f843.png'>

ここまで正しく設定できていれば、「成功」が表示されます。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/7e15de30-975a-1ba5-904b-f3a3abf3b6d3.png'>

<details><summary>うまくいかない場合の対処を表示</summary>

以下のようにタイムアウトが発生している場合、サーバーがスリープ状態になっている可能性があります。
少し待ってからやり直すか、Renderのダッシュボードから、サービスを再起動してください。
※Renderの無料枠では、一定時間Webサービスが使われない状態にあると、自動的にスリープになります。
Webサービスへのリクエストを受け取ると、起動状態になります。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/72edaf8a-f88d-289d-103e-d1d5ebf248b2.png'>

Renderのダッシュボードから再起動する方法はこちらです。
ダッシュボード右側の「Manual Deploy」を押下し、「Restart service」を選択します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/ebb9bad4-d079-81de-e590-30a255cc0505.png'>

また、以下のように「200以外のHTTPステータスコードが返されました。」となっている場合は、今までの何らかの設定を誤っている可能性があります。
設定しているURLや、実装の内容を確認してください。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/efd9f2e8-a95d-1a09-56bb-1f547510b3d1.png'>

</details>


次に「Webhookの利用」をオンにします。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/a7d5f530-8e13-2fa7-7ce4-d64ccfdbb640.png'>

### LINE公式アカウント機能の設定
応答メッセージ、あいさつメッセージを無効にします。
編集をクリックして、編集画面へ遷移します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/5d2ea062-48f8-3f0f-aa76-10d39b9ea9ea.png'>

応答メッセージ、あいさつメッセージをオフにしてください。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/1c8c8ecc-48c1-0f9f-3293-dba18f9031e9.png'>

### チャネルアクセストークン発行
チャネルアクセストークンはMessagingAPIの機能を使うために必要です。
「発行」を押下します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/fb9e9050-2439-a6f6-9abb-486ba0bf8ba5.png'>

発行されたトークンは外部へ公開しないようにしてください。
こちらをコピーしておきます。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/fd8e3a72-f357-002c-fdf9-63717f217167.png'>


### チャネルアクセストークンの設定
Renderのダッシュボードに戻ります。
左側のペインから「Environment」を選択します。

<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/f50fe79f-f512-18d4-e593-6c4913342e10.png'>

後で設定すると言っていた環境変数を設定します。
「Key」にはソースコードで取得対象にした変数名（LINE_ACCESS_TOKEN）
「Value」には先ほどコピーしたチャネルアクセストークンを設定します。
設定が完了したら「Save Changes」を押下します。

### 友だち追加
ここまできたらほぼ全ての設定は完了です。
LINE Develpersに戻り、Messaging　API設定の上部にあるQRコードを読み込んで、今回作成したボットをLINEの友達に追加します。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/1b9ab677-5f83-1dba-d409-fee4ca9fa78a.png'>

任意のメッセージを送ると、自動でメッセージが返ってきます。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/e1e8519d-1738-4263-00b0-3249eec592a1.png'>

## まとめ
かなり長くなってしまいましたが、LINEのアカウント周りの設定から、Renderによるサービスの作成、処理の実装と一気に進めました。
一度作ってしまえば、そんなに難しくなくボットを作れることがわかりました。

今回は自動応答の処理でしたが、公式アカウント側からプッシュメッセージを送ったり、ユーザ情報の取得をしたりすることもできるようです。

いろいろなAPIの挙動を覚え、実務に生かせるようにしたいです。

## 参考にしたサイト
* [チュートリアル - 応答ボットを作る](https://developers.line.biz/ja/docs/messaging-api/nodejs-sample/)