GASで毎朝その日の予定とかを通知するLINE Bot [第3回]

# この記事について

前回の続きです。初回でご紹介したコードの解説をしていきます。知識のある方にはつまらない記事になるかと思います。

+ [第1回](//qiita.com/items/634bf26f7d1cb30e8944)
+ [第2回](//qiita.com/items/5d5925c295b88d79f882)
+ 第3回
 + [祝日の取得](#祝日の取得)
 + [天気情報の取得](#天気情報の取得)
+ [第4回](//qiita.com/items/64315d311d44275465af)

# V8 ランタイムにする理由

`let`，`const`やアロー関数などといった、現在では標準的になったコードを使用するためです。
また、少しでも動作が低負荷でメモリも消費せず標準的なコードにするための選択でもあります。
解説中にV8 ランタイムからの機能が登場した際には、<sup>V8</sup> と付けることにします。

# コード解説
それでは、解説をしていきます。

```javascript
  // Change these variables fit to your condition
  const garbage = [0, '資源ごみ', '可燃ごみ', 'カン・ビン', '不燃ごみ', '可燃ごみ', 'プラスチックごみ'];
  const area = '13101';
  
  const d = new Date();
  const today = (d.getMonth() + 1) + '月' + d.getDate() + '日（' + '日月火水木金土'[d.getDay()] + '）';
```

最初にユーザに変更してもらう項目、実行時の日付などを宣言しておきます。

`'日月火水木金土'[d.getDay()]`
変わっている箇所と言えばここでしょうか。文字列は文字コードの配列を扱いやすくしたものなので、所詮は配列と同じように処理できます。

```js
  // これと同じことをしている
  const array = ['日', '月', '火', '水', '木', '金', '土'];
  const d = new Date();
  const today = (d.getMonth() + 1) + '月' + d.getDate() + '日（' + array[d.getDay()] + '）';
```

### 祝日の取得

```js
  const [event_holiday] = CalendarApp.getCalendarById('ja.japanese#holiday@group.v.calendar.google.com').getEventsForDay(d);
  const holiday = event_holiday ? event_holiday.getTitle() : '';
```
GASの拡張サービス`CalendarApp`を使用します。
`getCalendarById(id)`でカレンダーを取得します。
Googleカレンダーには、公式で祝日のカレンダーが存在します。
日本の祝日には、`ja.japanese#holiday@group.v.calendar.google.com`というIDのカレンダーが用意されているので、これを使います。
`getEventsForDay(day)`でその日のイベントを取得します。2つ以上の祝日が同じ日に来ることはないという前提で、`event_holiday`にはこの配列の1つ目の値を取り出して代入しています。

`event_holiday`の宣言方法に、**分割代入**<sup>V8</sup> を使用しています。配列の任意の要素を簡潔な表記で変数に代入する機能です。

```js:分割代入
let array = [3, 5, 7, 9, 11];
let [first, second, third] = array;
// first => 3, second => 5, third =>7

let [saisyo] = array; // 3
// これと同じ
let saisyo = array[0]; // 3
```

また、`holiday`には、三項演算子を使用しています。祝日がない日の`event_holiday`は空配列の中身を参照するため`undefined`になります。これは条件式上では`false`と同じ挙動をします（0も同じ）。

```js:三項演算子
let check = false;
let result = check ? "trueだよ" : "falseだよ"; // "falseだよ"

check = undefined; // 0, nullなどでも同じ
result = check ? "trueだよ" : "falseだよ"; // "falseだよ";
```

### 天気情報の取得

```js
  let content = UrlFetchApp.fetch('https://static.tenki.jp/static-api/history/forecast/' + area + '.js').getContentText();
  content = JSON.parse(content.substring(content.indexOf('(') + 1, content.indexOf(');')));
  let {max_t: temp_h = "不明", min_t: temp_l = "不明", t: weather = "不明"} = content;
  
  const words = {
    '時々': '|',
    '一時': '|',
    'のち': '»',
    '晴': '☀',
    '曇': '☁',
    '雨': '☔',
    '雪': '⛄'
  };
  for (let key in words) {
    weather = weather.replace(key, words[key]);
  }
```

拡張サービス`UrlFetchApp`を使用します。
`fetch(url, option)`でHTTPリクエストを送信することができます。第2引数のない状態では、特にオプションのないGETリクエストになります。リンク先のデータをそのままもらいます。
`UrlFetchApp.fetch(url, option)`ではレスポンスデータなるものが返ってきます。データを見たいので`getContentText()`で文字列として取得します。

>天気情報の取得には[tenki.jp](//tenki.jp)のデータの変なところから引っ張ってきています。
自分で使っていたときは気象庁のページのWebスクレイピングだったのですが、公開するにあたり一般化が非常に困難だと判明したので代替策を探っていました。

>当初はWebスクレイピングにしようと思っていたのですが、ページのサイズが大きいので高負荷になってしまう懸念があり、APIを探し回っていたところたまたま発見しました。
tenki.jpで天気予報を見ていると画面上部に自分が最近閲覧した地域の天気予報が小さく表示されるんですが、それを取得するAPIがありました。

>URLのフォーマットは
`https://static.tenki.jp/static-api/history/forecast/XXXXX.js`
XXXXXには標準地域コードが入ります。で、返ってくるのが
`'__r__XXXXX({"i":"画像番号","j":"標準地域コード","max_t":"最高気温","min_t":"最低気温","n":"地域名","p":"降水確率","t":"天気"});'`
画像番号はサイト表示用の画像の番号ですね。
`https://static.tenki.jp/images/icon/forecast-days-weather/XX.png`で表示できるみたい。
今回はこのデータをありがたく使わせていただくことにしました。

`substring`で両端の要らない文字を捨てて、`JSON.parse(str)`（文字列をJSONデータに）に通すと、以下のデータができあがります。

```json:content
{
  "i":"画像番号",
  "j":"標準地域コード",
  "max_t":"最高気温",
  "min_t":"最低気温",
  "n":"地域名",
  "p":"降水確率",
  "t":"天気"
}
```

ここでまた、分割代入の登場です。
`let {max_t: temp_h = "不明", min_t: temp_l = "不明", t: weather = "不明"} = content;`
ここでは、さらに高度なことをやっています。
前提として、以下のJSONデータを宣言しておきます。

```js
let json = {a: 1, b: 3, c: 5};
```
<br>**連想配列やJSONデータの分割代入**

```js
let {a, b, c} = json;
// a => 1, b => 3, c => 5
```

配列の分割代入では`[ ]`を使用して宣言しましたが、連想配列（JSON）では`{ }`に入れて宣言します。
<br>**任意の名前の変数への代入**

```js
let {a, b: beta, c: charlie} = json;
// a => 1, beta => 3, charlie => 5
```
<br>**変数の初期値**<sup>V8</sup> **の設定**
変数の初期値は他にも関数などで適用可能です。

```js
let {a, b: beta, c: charlie, d: delta = 10} = json;
// a => 1, beta => 3, charlie => 5, delta => 10
```

<br>
次に、天気の内容を絵文字に変換します。ここでは、**`for-in`関数**<sup>V8</sup> を使用します。
先程の`json`を使ってご説明します。

```js:for-in
let s = '';
let x = 0;
for (let key in json) {
  s += key;
  x += json[key];
}
// s => 'abc', x => 9
```

これは、配列（連想配列含む）の要素ひとつひとつに対して処理を行う反復処理です。
配列の要素の数だけ実行されます。`key`には要素のインデックスまたはキーが入ります。
今回は連想配列なのでキーが入ります。`'晴'`とか。

<br>
次回に続きます。