LINEのBotでキャンセル枠の通知

## はじめに
イベントのキャンセル枠を確認するのにいちいちイベントサイトを確認していたのですが（キャンセル待ちができないイベント…），勉強がてらLineのBotくんにキャンセル枠を通知してもらう仕組みを作ってみました，主に[Messaging API - クイックスタート](https://developers.line.biz/ja/docs/messaging-api/) と [Heroku を使って無料でアプリを開発](https://jp.heroku.com/free)を参考にしました．だらだらと実装してしまいましたが，難しいことはなく簡単でした．

### 処理イメージ
![Screen Shot 2018-11-20 at 1.05.06.png](https://qiita-image-store.s3.amazonaws.com/0/55110/2bcdbd79-5924-f23d-5910-29711efdea66.png)

## ファイル構成
参考までに，ソースコードは[GitHub](https://github.com/xshirade/line/tree/qiita)で公開しています．[express-generator](https://www.npmjs.com/package/express-generator)のテンプレートから不必要な部分（UIなど）を削除し，後ほど紹介するcron.jsやline.jsなどを少し加えただけです（[Procfile](https://devcenter.heroku.com/articles/procfile)は，Herokuのためのファイル）．

```bash
$ tree -I node_modules
.
├── Procfile
├── README.md
├── app.js
├── bin
│   └── www
├── lib
│   ├── cron.js
│   └── line.js
├── package-lock.json
└── package.json
```

## 主なコード
### app.js
サーバ本体のコードです．エンドポイントの`/health`は，無料のヘルスチェックサービスの[Uptime Robot](https://uptimerobot.com/)から定期的にアクセスされます．無料枠のHeroku（[Free Dyno Hours](https://devcenter.heroku.com/articles/free-dyno-hours#dyno-sleeping)）がスリープしないようにするためです．

```javascript:app.js
const express = require('express');
const app = express();
require('./lib/cron');

app.get('/health', (_, res) => {
    res.sendStatus(200);
});

module.exports = app;
```

### lib/line.js
LINEの[Message API](https://developers.line.me/ja/reference/messaging-api/)にアクセスするためのクライアントです．`channelAccessToken`と`channelSecret`は，[Messaging API - クイックスタート](https://developers.line.biz/ja/docs/messaging-api/)を参考に取得できます．

```javascript:lib/line.js
const line = require("@line/bot-sdk");

const credential = {
    channelAccessToken: process.env.LINE_ACCESS_TOKEN,
    channelSecret: process.env.LINE_CHANNEL_SECRET 
};
const client = new line.Client(credential);

module.exports = {
    client
}
```

### lib/cron.js
[node-cron](https://www.npmjs.com/package/node-cron)を用いて，Herokuサーバから定期的にイベントページへアクセスさせます（毎日8, 12, 18, 22時にアクセスし確認するように設定）．イベントのキャンセル枠は，（雑ですが）タイトルに”空”という文字が含まれるかどうかをみることで確認します．キャンセルがある場合は，ユーザ（`USER_ID`）にイベントタイトルと申し込みページのURLをプッシュします．勉強目的で開発したため宛先を固定していますが，もう少し手を加えてユーザごとに監視するイベントやタイミングなどをデータベースで管理するということもできるかなと思っています．自分の`USER_ID`は，LINE Developersのチャネル基本設定の最後にあります．

```javascript:lib/cron.js
const request = require('request');
const cron = require('node-cron');
const cheerio = require('cheerio');
const { client } = require('./line');

cron.schedule('0 8,12,18,22 * * *', () => {
    // イベントページにアクセスします．
    request({
        method: 'GET',
        uri: process.env.URL
    }, (err, res, body) => {
        if (err || res.statusCode !== 200) {
            console.error(err || res.statusCode);
        }
        // イベントタイトルを取得します．
        const $ = cheerio.load(body);
        const title = $('.event_name').text().replace(/\s+/g, '');
        if (title.match(/空/) !== null) {
            // キャンセルがある場合，ユーザへメッセージを送信します．
            client.pushMessage(process.env.USER_ID, {
                type: 'text',
                text: `${title} - ${process.env.URL}`
            });
        } else {
            // 確認用のログです．意味はありません．
            console.log('キャンセルはありませんでした');
        }
    });
});
```

## さいごに
日常的に使っているLINEに，キャンセル通知がくることで確認するためのストレスが減りました．なかなか良いです．他にも通知させると楽になりそうなものは手作りしてみようかなという気持ちになっています．今回利用したHerokuやUptime Robotは無料ですし，開発する時間と意欲さえあればという感じです