バイクで走るタイムリミットをLINE_BotとWeb_APIを組み合わせて実装してみた～

#近ごろ私達は～いい感じ♪
これが私の生きる道ですね！
見出しだけでピンきて懐かしさを感じた方はおそらく30代Overでしょう笑
（何故いきなり冒頭に？といった疑問は記事を読み進めるとわかりますので、お付き合いください～）

さてさて、
10月からプログラミングを習い始めて毎日勉強中の私ですが、
LINE_BotとWeb_APIを組み合わせて何か試したいと思い、
大好きなバイクを例に作ってみました！

#これが私の走る道
学生時代からバイクで峠を走るのが大好きで、ヒマさえあれば埼玉県飯能市の自宅から
奥多摩湖（東京都西多摩郡）まで友人と走りに行っています。
しかし、1車線の道が続く田舎道なので日中は渋滞ばかりで気持ちよく走れません(´；ω；`)ｳｩｩ
特に朝方と夕方は渋滞がひどく、それなら真夜中に気持ちよく走ろう！
ということで、夜が更けてから朝方までひたすら峠を走るということを、
学生時代はよくやっていました（あの頃は若かった、、、）

奥多摩の夜は、空気が澄んでいて星が本当に綺麗なんです(￣ー￣)
![奥多摩_星空.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2049267/a92fb2e6-cc64-b0da-1914-0753a2b7536a.jpeg)
出典：[YAMA HACK 東京でも星が見られる！？奥多摩のおすすめの観察スポットとタイミング](https://yamahack.com/2848)

#これが私の求む物
暖かい時期は日の出が早く、日が昇るにつれて交通量も増えます。
**朝の渋滞に巻き込まれる前に帰る！というのが毎回のミッション**で、
渋滞を回避するために、日の出・日の入りがわかる[Sunset and sunrise times API](https://sunrise-sunset.org/api)を利用しました。

奥多摩から自宅まで約40分の道のりなので、
<font color="Blue">**日の出の時間がわかれば家に着く時間を逆算して奥多摩を出発、
渋滞が起きる前には清々しい気持ちで家に着く**</font>といった魂胆です笑

LINE_BotとWeb_APIを組み合わせて、奥多摩を出発しなけばならない時間を逆算できるようにします～

#環境
Visual Studio Code v1.60.2
node v16.10.0
npm 7.21.1

#Botに没頭、できてホッと。
以下、コードです。

```js:server3.js
'use strict';

// ########################################
//               初期設定など
// ########################################

// パッケージを使用します
const express = require('express');
const line = require('@line/bot-sdk');
const axios = require('axios');

// ローカル（自分のPC）でサーバーを公開するときのポート番号です
const PORT = process.env.PORT || 3000;

// Messaging APIで利用するクレデンシャル（秘匿情報）です。
const config = {
    channelSecret: '作成したBotのチャネルシークレット',
    channelAccessToken: '作成したBotのチャネルアクセストークン'
};

// ########## ▼▼▼ サンプル関数 ▼▼▼ ##########
const sampleFunction = async (event) => {
    // ユーザーメッセージが「日の出」か「日の入り」かどうか
    if (event.message.text !== '今日のタイムリミットは？') {
        return client.replyMessage(event.replyToken, {
            type: 'text',
            text: '「今日のタイムリミットは？」と話しかけてね'
        });
    } else {
        // 「リプライ」を使って先に返事しておきます
        await client.replyMessage(event.replyToken, {
            type: 'text',
            text: 'ちょいとお待ちを…'
        });
        
        let pushText = '';
        try {
            // axiosで日の出日の入り時刻のAPIを叩きます（少し時間がかかる・ブロッキングする）
            const res = await axios.get('https://api.sunrise-sunset.org/json?lat=35.7772463&lng=138.9766782');
            // 取得できるのはUTCなので日本時間（+9時間）になおす
            const utc_time = res.data.results.sunrise;
            // '時', '分', '秒 PM' に分割する
            const tm_split = utc_time.split(':');
            // '時' を9時間進めて12時間戻す（13時を過ぎないようにする）
            const jp_hour = Number(tm_split[0]) + 9 - 12;
            // '秒 PM' を '秒' だけにする
            const sec = tm_split[2].split(' ')[0];
            // 再構成する
            const time_string = `${jp_hour}時${tm_split[1]}分${sec}秒`;
            pushText = `今日のタイムリミットは${time_string}です！渋滞前に帰りましょう！`;
        } catch (error) {
            pushText = '検索中にエラーが発生しました。ごめんね。';
            // APIからエラーが返ってきたらターミナルに表示する
            console.error(error);
        }

        // 「プッシュ」で後からユーザーに通知します
        return client.pushMessage(event.source.userId, {
            type: 'text',
            text: pushText,
        });
    }
};
// ########## ▲▲▲ サンプル関数 ▲▲▲ ##########

// ########################################
//  LINEサーバーからのWebhookデータを処理する部分
// ########################################

// LINE SDKを初期化します
const client = new line.Client(config);

// LINEサーバーからWebhookがあると「サーバー部分」から以下の "handleEvent" という関数が呼び出されます
async function handleEvent(event) {
    // 受信したWebhookが「テキストメッセージ以外」であればnullを返すことで無視します
    if (event.type !== 'message' || event.message.type !== 'text') {
        return Promise.resolve(null);
    }
    // サンプル関数を実行します
    return sampleFunction(event);
}

// ########################################
//          Expressによるサーバー部分
// ########################################

// expressを初期化します
const app = express();

// HTTP POSTによって '/webhook' のパスにアクセスがあったら、POSTされた内容に応じて様々な処理をします
app.post('/webhook', line.middleware(config), (req, res) => {
  
  // 検証ボタンをクリックしたときに飛んできたWebhookを受信したときのみ以下のif文内を実行
  if (req.body.events.length === 0) {
    res.send('Hello LINE BOT! (HTTP POST)'); // LINEサーバーに返答します（なくてもよい）
    console.log('検証イベントを受信しました！'); // ターミナルに表示します
    return; // これより下は実行されません
  } else {
    // 通常のメッセージなど … Webhookの中身を確認用にターミナルに表示します
    console.log('受信しました:', req.body.events);
  }

  // あらかじめ宣言しておいた "handleEvent" 関数にWebhookの中身を渡して処理してもらい、
  // 関数から戻ってきたデータをそのままLINEサーバーに「レスポンス」として返します
  Promise.all(req.body.events.map(handleEvent)).then((result) => res.json(result));
});

// 最初に決めたポート番号でサーバーをPC内だけに公開します
// （環境によってはローカルネットワーク内にも公開されます）
app.listen(PORT);
console.log(`ポート${PORT}番でExpressサーバーを実行中です…`);
```
LINE_BotとWeb_APIと習ったので、様々なAPIを叩いてみたものの、うまく動かすことが出来ず。
（動かすことができたら記事UPしますね～）
そこは引き続き頑張るとして、今回はこんな感じのコードです！

緯度経度を奥多摩湖の場所に変更したり、出力文言を変更したり、
使っているコードを解読しながら、数時間没頭してやっと完成！

#結果
![LINE Bot_Web API.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2049267/992e5ce8-cf5b-9bf8-4715-d2330549bc19.gif)

「今日のタイムリミットは？」と入力すると、
**「ちょいとお待ちを、、、」
「今日のタイムリミットは〇時〇分〇秒です！渋滞前に帰りましょう！」**
とリプライメッセージが届きます。
この時間が奥多摩湖の日の出の時間ですね！
適当な文字を入れても質問文字を促してくれるので大丈夫です。

バイク乗りの本音で言うと、朝が冷え込む時期になると手先の感覚皆無で、
スマホ打つどころではなくなるので、実際使う時の入力は「あ」にしたいと思います～！

他にも面白そうなAPIがたくさんありますので、しばらくBotに没頭していきます（´-`）.｡oO

参考：[APIマーケットプレイス - 10,000以上のRest APIにアクセス | Rakuten RapidAPI](https://api.rakuten.net/marketplace)
参考：[APIを探して、試して、使える - APIbank](https://www.apibank.jp/ApiBank/api)

**これが私に活きるか未知(￣ー￣)**
うそです、使いこなせるように頑張ります。

最後まで読んでいただき、ありがとうございました！