Line Botへ現在の天気を表示 ～天気APIを使用しLINE BOTへ自動返信～

LINE Botの勉強を始め、おうむ返し　は作れました。更に応用することを考えたときにAPIとの連携ができることを知り、作ってみました。

下準備
　①　まずはこちらの記事を参考にLine Botを作る。
　　　　おうむ返しまで完了させる。
　　　　　https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d
　②　obnizを扱うので、Visual Studio Codeのターミナルに、npm install obniz
　　　（今後マトリックスLEDを扱うならば　npm install canvas　も）を入力し
　　　　実行しておく。
　③　obnizスターターキットの用意
　　　　obnizの端子にパーツを接続
　　　　　　LED信号：gnd:0, green:1, yellow:2, red:3
　④　OpenWeatherMapのAPIキーを取得（以下のHPを参照）
　　　　　https://yuukiyg.hatenablog.jp/entry/2019/11/17/182410

実行
　　Visual Studio Codeに、以下のコードを入力し、各サーバーを立ち上げ、
　　LINE Developersの設定を行う。

　①ターミナルに、
　　cd mylinebot　　を実行（mylinbotフォルダ内に入る）。
　　node server.js　を実行。

　②もう一つターミナルを開いて、
　　npx ngrok http 3000（npm ngrok http 3000　の方もいる）を実行。

　③npx ngrok http 3000（又はnpm ngrok http 3000）サーバーに表示された
　　URL(HTTPSの方）をコピーして、LINE DEpelopersのHPの、Messaging APIタグの、
　　Webhook設定のWebhook URLに張り付け、後ろに「/webhook」をつける。


```javascript
//【”天気”と入れると、今の天気・雲の様子・気温を返し、obinzの天気に対応するLED信号を点灯させる。】
'use strict';

const Obniz = require("obniz");
var obniz = new Obniz("xxxx-xxxx"); //obninzのIDを入力
obniz.onconnect = async function () { 
          //event(この場合はLineへの入力)よりも先にobniz.onconnectをしておく必要がある。
          //全体をobnizの"{ }"で括ると扱いやすい様子。
    var light = obniz.wired("Keyestudio_TrafficLight", {gnd:0, green:1, yellow:2, red:3});

  const express = require('express');
  const line = require('@line/bot-sdk');
  const PORT = process.env.PORT || 3000;

  const config = {
      channelSecret: '{自分のchannelSecret}',
      channelAccessToken: '{自分のchannelAccessToken}'
  };

  const app = express();

  app.get('/', (req, res) => res.send('Hello LINE BOT!(GET)')); //ブラウザ確認用(無くても問題ない)
  app.post('/webhook', line.middleware(config), (req, res) => {
      console.log(req.body.events);

      //ここのif分はdeveloper consoleの"接続確認"用なので削除して問題ないです。
      if(req.body.events[0].replyToken === '00000000000000000000000000000000' && req.body.events[1].replyToken === 'ffffffffffffffffffffffffffffffff'){
          res.send('Hello LINE BOT!(POST)');
          console.log('疎通確認用');
          return; 
      }

      Promise
        .all(req.body.events.map(handleEvent))
        .then((result) => res.json(result));
  });

  const client = new line.Client(config);

  async function handleEvent(event) {
    if (event.type !== 'message' || event.message.type !== 'text') {
      return Promise.resolve(null);
    }

    //返信コードはここから
      if (event.message.text === '天気'){

        const axios = require('axios');
        const main = async () => {
          try {
            let cname = 'Tokyo'
            const res = await const res = await axios.get('http://api.openweathermap.org/data/2.5/weather?q=Tokyo&units=metric&APPID={自分のAPIキー}');  // URLを書き換える
            console.log(res.data); // データ全体を表示する(なくてもOK)
              
            let currentWeather = res.data.weather[0].main;
            let description = res.data.weather[0].description;
            let temp = res.data.main.temp;
            let temp_max = res.data.main.temp_max;
            let temp_min = res.data.main.temp_min;
            let city = res.data.name;
    
            var currentDate=new Date();
            var y=currentDate.getFullYear();
            var mo=currentDate.getMonth();
            var d=currentDate.getDate();
            var h=currentDate.getHours();
            var m=currentDate.getMinutes();
            var today=y + '年' + mo + '月' + d + '日'
            var str=h+":"+m;
    
            const RED_WEATHERS = [
              "Rain",
              "Snow",
              "Thunderstorm",
              "Drizzle",
              "Fog",
              "Squall"
            ];
            const YELLOW_WEATHERS = [
              "Clouds",
              "Mist",
              "Smoke",
              "Dust",
              "Haze",
              "Sand",
              "Ash",
              "Tornado"
            ];
            const GREEN_WEATHERS = ["Clear"];

            let weather = res.data.weather[0].main;
            let weather_ja = '晴れ'
              switch (weather){
                  case 'Clear':
                    weather_ja = '晴れ';
                    break;
                  case 'Clouds':
                    weather_ja = 'くもり';
                    break;
                  case 'Mist':
                    weather_ja = 'もや';
                    break;
                  case 'Smoke':
                    weather_ja = '煙';
                    break;
                  case 'Dust':
                    weather_ja = 'ほこり';
                    break;
                  case 'Haze':
                    weather_ja = 'かすみ';
                    break;
                  case 'Sand':
                    weather_ja = '砂';
                    break;
                  case 'Ash':
                    weather_ja = '灰';
                    break;
                  case 'Tornado':
                    weather_ja = '竜巻';
                    break;
                  case 'Rain':
                    weather_ja = '雨';
                    break;
                  case 'Snow':
                    weather_ja = '雪';
                    break;
                  case 'Thunderstorm':
                    weather_ja = '雷雨';
                  case 'Drizzle':
                    weather_ja = '霧雨';
                    break;
                  case 'Fog':
                    weather_ja = '霧';
                    break;
                  case 'Squall':
                    weather_ja = 'スコール';
                    break;
              }

            let description_ja = 'なし'
              switch (description){
                  case 'thunderstorm with light rain':
                    description_ja = '小雨で雷雨';
                    break;
                  case 'thunderstorm with rain':
                    description_ja = '雨で雷雨';
                    break;
                  case 'thunderstorm with heavy rain':
                    description_ja = '大雨で雷雨';
                    break;
                  case 'light thunderstorm':
                    description_ja = '軽い雷雨';
                    break;
                  case 'thunderstorm':
                    description_ja = '雷雨';
                    break;
                  case 'heavy thunderstorm':
                    description_ja = '激しい雷雨';
                    break;
                  case 'ragged thunderstorm':
                    description_ja = '不規則な雷雨';
                    break;
                  case 'thunderstorm with light drizzle':
                    description_ja = '軽い霧雨と雷雨 ';
                    break;
                  case 'thunderstorm with drizzle':
                    description_ja = '霧雨と雷雨';
                    break;
                  case 'thunderstorm with heavy drizzle':
                    description_ja = '激しい霧雨で雷雨';
                    break;
                  case 'light intensity drizzle':
                    description_ja = '弱い霧雨';
                    break;
                  case 'drizzle':
                    description_ja = '霧雨';
                    break;
                  case 'heavy intensity drizzle':
                    description_ja = '強い霧雨';
                    break;
                  case 'light intensity drizzle rain':
                    description_ja = '弱い霧雨';
                    break;
                  case 'drizzle rain':
                    description_ja = '霧雨';
                    break;
                  case 'heavy intensity drizzle rain':
                    description_ja = '激しい霧雨';
                    break;
                  case 'shower rain and drizzle':
                    description_ja = 'にわか雨と霧雨';
                    break;
                  case 'heavy shower rain and drizzle':
                    description_ja = '強いにわか雨と霧雨';
                    break;
                  case 'shower drizzle':
                    description_ja = 'にわか霧雨';
                    break;
                  case 'light rain':
                    description_ja = '小雨';
                    break;
                  case 'moderate rain':
                    description_ja = '中程度の雨';
                    break;
                  case 'heavy intensity rain':
                    description_ja = '大雨';
                    break;
                  case 'very heavy rain':
                    description_ja = '強い大雨';
                    break;
                  case 'extreme rain':
                    description_ja = 'とても強い大雨';
                    break;
                  case 'freezing rain':
                    description_ja = '凍雨';
                    break;
                  case 'light intensity shower rain':
                    description_ja = '弱いにわか雨';
                    break;
                  case 'shower rain':
                    description_ja = 'にわか雨';
                    break;
                  case 'heavy intensity shower rain':
                    description_ja = '激しいにわか雨';
                    break;
                  case 'ragged shower rain':
                    description_ja = '不規則なにわか雨';
                    break;
                  case 'light snow':
                    description_ja = '軽い雪';
                    break;
                  case 'Snow':
                    description_ja = '雪';
                    break;
                  case 'Heavy snow':
                    description_ja = '大雪';
                    break;
                  case 'Sleet':
                    description_ja = '霙(みぞれ)';
                    break;
                  case 'Light shower sleet':
                    description_ja = '弱いにわか霙(みぞれ)';
                    break;
                  case 'Shower sleet':
                    description_ja = 'にわか霙(みぞれ)';
                    break;
                  case 'Light rain and snow':
                    description_ja = '小雨と雪';
                    break;
                  case 'Rain and snow':
                    description_ja = '雨と雪';
                    break;
                  case 'Light shower snow':
                    description_ja = '弱いにわか雪';
                    break;
                  case 'Shower snow':
                    description_ja = 'にわか雪';
                    break;
                  case 'Heavy shower snow':
                    description_ja = '強いにわか雪';
                    break;
                  case 'mist':
                    description_ja = '靄（もや）';
                    break;
                  case 'Smoke':
                    description_ja = '煙';
                    break;
                  case 'Haze':
                    description_ja = '霞（かすみ）';
                    break;
                  case 'sand/ dust whirls':
                    description_ja = '砂/塵旋風';
                    break;
                  case 'fog':
                    description_ja = '霧（きり）';
                    break;
                  case 'sand':
                    description_ja = '砂';
                    break;
                  case 'dust':
                    description_ja = ' 塵';
                    break;
                  case 'volcanic ash':
                    description_ja = '火山灰';
                    break;
                  case 'squalls':
                    description_ja = 'スコール';
                    break;
                  case 'tornado':
                    description_ja = '竜巻';
                    break;
                  case 'clear sky':
                    description_ja = '澄んだ空';
                    break;
                  case 'few clouds':
                    description_ja = '少しの雲';
                    break;
                  case 'scattered clouds':
                    description_ja = '散乱雲';
                    break;
                  case 'broken clouds':
                    description_ja = '壊れた雲';
                    break;
                  case 'overcast clouds':
                    description_ja = '曇り雲';
                    break;
              }
　　　　　　　　　　　　　　　//LINE Botへの返信　の後に処理を記載すると、「到達できないコード」とエラーがでるので、先に記載をする。
              if (currentWeather === undefined || currentWeather === null) {
                light.green.off();
                light.yellow.off();
                light.red.off();

              }
              if (RED_WEATHERS.includes(currentWeather) === true) {
                  light.red.on();
      
              } else if (YELLOW_WEATHERS.includes(currentWeather) === true) {
                  light.yellow.on();
      
              } else if (GREEN_WEATHERS.includes(currentWeather) === true) {
                  light.green.on();
      
              } else {
                light.green.off();
                light.yellow.off();
                light.red.off();
              }

            let mes = '';  //LINE Botへの返信
            mes = today + ' ' + str + ' ' + city + '\n' + '現在の天気は「'+ weather_ja +'」です。\n'+ '詳細：' + description_ja +'\n' + '気温は' + temp  +'℃です。';
            
            return client.replyMessage(event.replyToken, {  
              type: 'text',
              text: mes,
            });  

          } catch (error) {
            console.error(error);
          }

        }
        main();
        
      }else if (event.message.text === 'オフ'){
        light.green.off();
        light.yellow.off();
        light.red.off();
      
      }else {
          return client.replyMessage(event.replyToken, {  
            type: 'text',
            text: '”天気” と入れてください。',
          }); 
      }
        //返信コードはここまで
  }
  app.listen(PORT);
  console.log(`Server running at ${PORT}`);
}  //obniz.onconnectの閉じカッコ。

```

下のコードは、上コードを作成するベースに使ったコード
（obnizと連携させる前の、LINE BotとAPIの連携のみのコード）

```javascript
//【”天気”と入れると、日時、今の天気・雲の様子・気温を返す。】
'use strict';

const express = require('express');
const line = require('@line/bot-sdk');
const PORT = process.env.PORT || 3000;

  const config = {
      channelSecret: '{自分のchannelSecret}',
      channelAccessToken: '{自分のchannelAccessToken}'
  };

const app = express();

app.get('/', (req, res) => res.send('Hello LINE BOT!(GET)')); //ブラウザ確認用(無くても問題ない)
app.post('/webhook', line.middleware(config), (req, res) => {
    console.log(req.body.events);

    //ここのif分はdeveloper consoleの"接続確認"用なので削除して問題ないです。
    if(req.body.events[0].replyToken === '00000000000000000000000000000000' && req.body.events[1].replyToken === 'ffffffffffffffffffffffffffffffff'){
        res.send('Hello LINE BOT!(POST)');
        console.log('疎通確認用');
        return; 
    }

    Promise
      .all(req.body.events.map(handleEvent))
      .then((result) => res.json(result));
});

const client = new line.Client(config);

async function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

  //返信コードはここから
    if (event.message.text === '天気'){

      const axios = require('axios');
      const main = async () => {
        try {
          let cname = 'Tokyo'
          const res = await axios.get('http://api.openweathermap.org/data/2.5/weather?q=Tokyo&units=metric&APPID={自分のAPIキー}');  // URLを書き換える
          console.log(res.data); // データ全体を表示する(なくてもOK)
            
          let currentWeather = res.data.weather[0].main;
          let description = res.data.weather[0].description;
          let temp = res.data.main.temp;
          let temp_max = res.data.main.temp_max;
          let temp_min = res.data.main.temp_min;
          let city = res.data.name;
  
          var currentDate=new Date();
          var y=currentDate.getFullYear();
          var mo=currentDate.getMonth();
          var d=currentDate.getDate();
          var h=currentDate.getHours();
          var m=currentDate.getMinutes();
          var today=y + '年' + mo + '月' + d + '日'
          var str=h+":"+m;
  
          let weather = res.data.weather[0].main;
          let weather_ja = '晴れ'
            switch (weather){
                case 'Clear':
                  weather_ja = '晴れ';
                  break;
                case 'Clouds':
                  weather_ja = 'くもり';
                  break;
                case 'Mist':
                  weather_ja = 'もや';
                  break;
                case 'Smoke':
                  weather_ja = '煙';
                  break;
                case 'Dust':
                  weather_ja = 'ほこり';
                  break;
                case 'Haze':
                  weather_ja = 'かすみ';
                  break;
                case 'Sand':
                  weather_ja = '砂';
                  break;
                case 'Ash':
                  weather_ja = '灰';
                  break;
                case 'Tornado':
                  weather_ja = '竜巻';
                  break;
                case 'Rain':
                  weather_ja = '雨';
                  break;
                case 'Snow':
                  weather_ja = '雪';
                  break;
                case 'Thunderstorm':
                  weather_ja = '雷雨';
                case 'Drizzle':
                  weather_ja = '霧雨';
                  break;
                case 'Fog':
                  weather_ja = '霧';
                  break;
                case 'Squall':
                  weather_ja = 'スコール';
                  break;
            }

          let description_ja = 'なし'
            switch (description){
                case 'thunderstorm with light rain':
                  description_ja = '小雨で雷雨';
                  break;
                case 'thunderstorm with rain':
                  description_ja = '雨で雷雨';
                  break;
                case 'thunderstorm with heavy rain':
                  description_ja = '大雨で雷雨';
                  break;
                case 'light thunderstorm':
                  description_ja = '軽い雷雨';
                  break;
                case 'thunderstorm':
                  description_ja = '雷雨';
                  break;
                case 'heavy thunderstorm':
                  description_ja = '激しい雷雨';
                  break;
                case 'ragged thunderstorm':
                  description_ja = '不規則な雷雨';
                  break;
                case 'thunderstorm with light drizzle':
                  description_ja = '軽い霧雨と雷雨 ';
                  break;
                case 'thunderstorm with drizzle':
                  description_ja = '霧雨と雷雨';
                  break;
                case 'thunderstorm with heavy drizzle':
                  description_ja = '激しい霧雨で雷雨';
                  break;
                case 'light intensity drizzle':
                  description_ja = '弱い霧雨';
                  break;
                case 'drizzle':
                  description_ja = '霧雨';
                  break;
                case 'heavy intensity drizzle':
                  description_ja = '強い霧雨';
                  break;
                case 'light intensity drizzle rain':
                  description_ja = '弱い霧雨';
                  break;
                case 'drizzle rain':
                  description_ja = '霧雨';
                  break;
                case 'heavy intensity drizzle rain':
                  description_ja = '激しい霧雨';
                  break;
                case 'shower rain and drizzle':
                  description_ja = 'にわか雨と霧雨';
                  break;
                case 'heavy shower rain and drizzle':
                  description_ja = '強いにわか雨と霧雨';
                  break;
                case 'shower drizzle':
                  description_ja = 'にわか霧雨';
                  break;
                case 'light rain':
                  description_ja = '小雨';
                  break;
                case 'moderate rain':
                  description_ja = '中程度の雨';
                  break;
                case 'heavy intensity rain':
                  description_ja = '大雨';
                  break;
                case 'very heavy rain':
                  description_ja = '強い大雨';
                  break;
                case 'extreme rain':
                  description_ja = 'とても強い大雨';
                  break;
                case 'freezing rain':
                  description_ja = '凍雨';
                  break;
                case 'light intensity shower rain':
                  description_ja = '弱いにわか雨';
                  break;
                case 'shower rain':
                  description_ja = 'にわか雨';
                  break;
                case 'heavy intensity shower rain':
                  description_ja = '激しいにわか雨';
                  break;
                case 'ragged shower rain':
                  description_ja = '不規則なにわか雨';
                  break;
                case 'light snow':
                  description_ja = '軽い雪';
                  break;
                case 'Snow':
                  description_ja = '雪';
                  break;
                case 'Heavy snow':
                  description_ja = '大雪';
                  break;
                case 'Sleet':
                  description_ja = '霙(みぞれ)';
                  break;
                case 'Light shower sleet':
                  description_ja = '弱いにわか霙(みぞれ)';
                  break;
                case 'Shower sleet':
                  description_ja = 'にわか霙(みぞれ)';
                  break;
                case 'Light rain and snow':
                  description_ja = '小雨と雪';
                  break;
                case 'Rain and snow':
                  description_ja = '雨と雪';
                  break;
                case 'Light shower snow':
                  description_ja = '弱いにわか雪';
                  break;
                case 'Shower snow':
                  description_ja = 'にわか雪';
                  break;
                case 'Heavy shower snow':
                  description_ja = '強いにわか雪';
                  break;
                case 'mist':
                  description_ja = '靄（もや）';
                  break;
                case 'Smoke':
                  description_ja = '煙';
                  break;
                case 'Haze':
                  description_ja = '霞（かすみ）';
                  break;
                case 'sand/ dust whirls':
                  description_ja = '砂/塵旋風';
                  break;
                case 'fog':
                  description_ja = '霧（きり）';
                  break;
                case 'sand':
                  description_ja = '砂';
                  break;
                case 'dust':
                  description_ja = ' 塵';
                  break;
                case 'volcanic ash':
                  description_ja = '火山灰';
                  break;
                case 'squalls':
                  description_ja = 'スコール';
                  break;
                case 'tornado':
                  description_ja = '竜巻';
                  break;
                case 'clear sky':
                  description_ja = '澄んだ空';
                  break;
                case 'few clouds':
                  description_ja = '少しの雲';
                  break;
                case 'scattered clouds':
                  description_ja = '散乱雲';
                  break;
                case 'broken clouds':
                  description_ja = '壊れた雲';
                  break;
                case 'overcast clouds':
                  description_ja = '曇り雲';
                  break;
            }

          let mes = '';
          mes = today + ' ' + str + ' ' + city + '\n' + '現在の天気は「'+ weather_ja +'」です。\n'+ '詳細：' + description_ja +'\n' + '気温は' + temp  +'℃です。';
          
          return client.replyMessage(event.replyToken, {  
            type: 'text',
            text: mes,
          });  

        } catch (error) {
          console.error(error);
        }
      }
      main();

    }else {
        return client.replyMessage(event.replyToken, {  
          type: 'text',
          text: '”天気” と入れてください。',
  }); 
  }
  //返信コードはここまで
}
app.listen(PORT);
console.log(`Server running at ${PORT}`);

```

仕様
　●LINE Botに、”天気”　と入力すると、今の天気・雲の様子・気温を返し、
　　obinzの天気に対応するLED信号を点灯させる。
　●”オフ”　と入力すると、LED信号を消灯する。
　●その他の文字を入力すると、「”天気” と入れてください。」と返す。

https://twitter.com/21HideK/status/1453371935899881473


工夫を要した箇所
　① 「//返信コードはここから」～「 //返信コードはここまで」の間にLine Botへ
　　　返信する内容を記載すれば実行できる。以前に作成した「Visual Studio Code
　　　で、obnizに天気の情報を表示させ、天気に対応するLED信号を光らせる」
　　　　　https://qiita.com/21HideK/items/9e8cf1a2ba3f1aae344e
　　　を利用したので、作成自体は難しくはなかった。しかし記入の仕方が少し
　　　違うだけでエラーが出てしまうのを修正するのに苦労した。

　②obniz.onconnectを記載する場所が分からず、何度もエラーを生じた。
　　下記HPより、event(この場合はLineへの入力)よりも先にobniz.onconnectを
　　しておく必要があることが分かり、解決した。

　③LINE Botへの返信の後に処理を記載すると、「到達できないコード」とエラー
　　がでるので、先に記載をする必要がある。


〔参考にしたHP〕
obnizとLineBotで防犯ツールを作ってみた（NO MORE XX 泥棒）
https://qiita.com/shima-07/items/e4d6a33c460da77e25ec?utm_campaign=popular_items&utm_medium=feed&utm_source=popular_items