Node.jsとMessaging APIでプッシュメッセージを送ろう

## はじめに
[前回](https://qiita.com/course_k/items/85bf9cfab971cb5b97ee)に引き続き、MessagingAPIの練習です。
今回はボット側から任意のタイミングでメッセージを送ることができるプッシュメッセージに挑戦します。

Node.jsとMessaging API、そしてボットのサーバーとしてRenderを使用します。
構築手順については[前回](https://qiita.com/course_k/items/85bf9cfab971cb5b97ee)の記事を参考にしてください。

また、本記事は以下のソースコードをベースにし、以降で紹介する処理を追加していくことで実装を進めます。
<details><summary>ソースコードを確認する</summary>

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

// 環境変数の取得
// ポート番号
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, res) => {
  res.send("HTTP POST request sent to the webhook URL!");
});

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

</details>


## ユーザーIDを取得する
プッシュメッセージの送信には、送信対象のユーザーIDが必要です。

ユーザーIDを取得する方法はいくつかありますが、今回はリクエストから取得することにします。
処理は以下のようになります。
```javascript
//ルーティングの設定-MessaginAPI
app.post("/webhook", (req, res) => {
    res.send("HTTP POST request sent to the webhook URL!");
    switch (req.body.events[0].type) {
        case "follow":
            const userData = { userId: req.body.events[0].source.userId }
            fs.writeFileSync('./user_data.json', JSON.stringify(userData));
    }
});
```
### 友達追加の判定
`/webhook`の処理にswitch文を追加しました。
ユーザーがボットアカウントを友だち追加したタイミングでユーザーIDを取得するようにしています。
友だち追加の判定はリクエストの`events`の`type`プロパティで行います。友達追加の場合は`follow`が設定されています。

### ユーザーIDの取得
ユーザーIDは`events`の`source`プロパティに`userId`として設定されています。
取得したユーザーIDはJSON形式のデータとしてファイルに保存しています。

本来はDBに保管するのでしょうが、簡易化のためファイルで管理します。

:::note warn
作成したファイルはRenderがデプロイされるたびに削除されるようです。
そのため、ファイルを使うためにはデプロイのたびに友達追加をする必要があります。
:::

## プッシュメッセージ送信処理を作成する
前回の自動応答メッセージとは異なり、任意のタイミングでメッセージを送るので、送信のトリガーが必要です。
`/push`というURLにアクセスがあった場合にメッセージを送るようにします。

### リクエストを受け取る
```javascript
app.get("/push", (req, res) => {
    res.send("HTTP POST request sent to the push URL!");
    const messages = [{ type: "text", text: "push message!", }];
    pushMessage(messages);
});
```
`/push`にアクセスがあった場合の処理を記述します。
URLを直接叩くので、getメソッドを呼び、その中に処理を書いていきます。
送信したい任意のメッセージを用意し、送信用のメソッドを呼んでいます。
`pushMessage`は後で用意するメソッドです。

### プッシュメッセージ送信処理を書く
実際にプッシュメッセージを送信する処理は以下のとおりです。
```javascript
function pushMessage(messages) {
    const HEADERS = {
        "Content-Type": "application/json",
        Authorization: "Bearer " + TOKEN,
    };

    const userData = JSON.parse(fs.readFileSync('./user_data.json', 'utf-8'));
    const userId = userData.userId;
    const dataString = JSON.stringify({
        to: userId,
        messages: messages,
    });

    const webhookOptions = {
        hostname: "api.line.me",
        path: "/v2/bot/message/push",
        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();
}
```
大まかに、
・リクエストヘッダーの設定
・リクエストボディの設定
・リクエスト送信に必要な情報の設定
・リクエストの送信からエラー設定まで
で成り立っており、基本的には前回の[ルーティングの設定-MessaginAPI](https://qiita.com/course_k/items/85bf9cfab971cb5b97ee#%E3%83%AB%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0%E3%81%AE%E8%A8%AD%E5%AE%9A-messaginapi)と同じです。

リクエストボディの設定、リクエスト送信に必要な情報の設定が異なるのでそれぞれ確認します

### リクエストボディの設定
リクエストボディに必要な情報は2つです。

#### to
ユーザーIDを設定します。

#### messages
送信したいメッセージを配列で設定します。

### リクエスト送信に必要な情報
応答メッセージで設定したときとは`path`のみが異なります。
あとは同じです。

#### path
`"/v2/bot/message/push"`を設定します。

ここまでできれば、送信処理は一通り完成しました。

完成したソースコードは以下のようになっているはずです。
<details><summary>完成形のコードを確認する</summary>

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

// 環境変数の取得
// ポート番号
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, res) => {
    res.send("HTTP POST request sent to the webhook URL!");
    switch (req.body.events[0].type) {
        case "follow":
            const userData = { userId: req.body.events[0].source.userId }
            fs.writeFileSync('./user_data.json', JSON.stringify(userData));
    }
});

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

app.get("/push", (req, res) => {
    res.send("HTTP POST request sent to the push URL!");
    const messages = [{ type: "text", text: "push message!", }];
    pushMessage(messages);
});

function pushMessage(messages) {
    const HEADERS = {
        "Content-Type": "application/json",
        Authorization: "Bearer " + TOKEN,
    };

    const userData = JSON.parse(fs.readFileSync('./user_data.json', 'utf-8'));
    const userId = userData.userId;
    const dataString = JSON.stringify({
        to: userId,
        messages: messages,
    });

    const webhookOptions = {
        hostname: "api.line.me",
        path: "/v2/bot/message/push",
        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();
}
```

</details>

## プッシュメッセージ送信
実際にプッシュメッセージが送信されるか確認します。

### 事前準備
まず、完成したコードをプッシュします。Renderの設定を行っていれば、自動でデプロイされます。
次に、デプロイの完了をRenderのダッシュボードから確認し次第、ボットアカウントを自分のラインアカウントに友だち追加しましょう。
このとき、内部でユーザーIDの取得が行われます。

### 送信する
自身のRenderのサイトを表示するURLの末尾に`/push`をつけてアクセスします。
※URLの例
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/ea2b3aa0-0933-3ef6-bd17-7875c51e1a4f.png'>

URLにアクセスすると、以下のようなテキストが表示されます。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/7339b73d-6125-56dc-f156-cd84e2a219b1.png'>

また、自身のラインアカウントに、ボットアカウントからメッセージが届いています。
<img width='70%' src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251137/9c886e31-1fe0-7eb5-8a9d-b8999ce1076f.png'>

## まとめ
プッシュメッセージを送信する方法について確認しました。
ユーザーIDさえ取得できれば、ほとんど応答メッセージと変わらないリクエストの形でプッシュメッセージを送信できることがわかりました。

Messaging APIには他にも様々な機能がありますが、基本的な形はおそらく大体同じなので、あとは自分のやりたいようにカスタマイズすることで自分だけのオリジナルボットアカウントが作れますね。

また、SDKも用意されているようなので、次はそれを使ってより簡単に開発ができるか試してみたいと思います。

## 参考にしたサイト
* [WebhookからユーザーIDを取得する | LINE Developers](https://developers.line.biz/ja/docs/messaging-api/getting-user-ids/#get-user-ids-in-webhook)
* [プッシュメッセージを送る | LINE Developers](https://developers.line.biz/ja/reference/messaging-api/#send-push-message)
* [フォローイベント | LINE Developers](https://developers.line.biz/ja/reference/messaging-api/#follow-event)