Windows機だけで、Gmailが届いたらGoogle homeがしゃべるようにしてみた

# はじめに
- Google homeをしゃべらせると言えば猫も杓子もRaspberry Piだ。
- しまいには、Google Home機能拡張キット「GHKit」まで登場している。
- 僕もGoogle homeを自発的にしゃべらせたいけど、Raspberry Piを持っていない。
- ならば、手持ちのWindows機だけで、何とかしたくなるのが貧乏人の性と言うもの。

# 前提
- Windows 10 pro バージョン1803

# 概要
- まず、大雑把な流れを考える。

1. IFTTTで、Gmailの新着タイトルと送り主をGoogle Drive Spreadsheetに書き込む
2. cronで毎分起動するnode.jpツールで Google Drive Spreadsheetから情報を取得する
3. 取得した新着タイトルと送り主を、「Google home喋らせサーバ」に送る

- 最後の怪しい「Google home喋らせサーバ」について補足する

1. Windows Subsystem for Linux(WSL)でubuntuを入れる
2. nodo.jsをWSLに入れる(最新にする)
5. google-home-notifierを入れる
6. example.jsを編集する
7. nohupで動かしておく

- 順番は適当に入れ替えつつ説明します

# IFTTTで、Gmailの新着タイトルと送り主をGoogle Drive Spreadsheetに書き込む
- [IFTTT](https://ifttt.com/)にアクセスしてsignupする
- My Appletsをクリック
- New Appletをクリック
- Thisをクリック
- Gmailをクリック
- Any new email in inboxをクリック
- Thatをクリック
- google sheetsをクリック
- Add row to spreadsheetをクリック
- Formatted rowを{{ReceivedAt}}|||{{FromAddress}}|||{{Subject}}}|||{{FromName}}に変更
- Create Actionをクリック
- Turn Onをクリック

# Windows Subsystem for Linux(WSL)でubuntuを入れる
- [Win]キーを押す
- 歯車マークをクリック
- アプリをクリック
- 「アプリと機能」の右側の関連設定の「プログラムと機能」をクリック
- 「プログラムと機能」の左側の「Windowsの機能の有効化または無効化」をクリック
- 「Windowsの機能の有効化または無効化」で「Windows Subsystem for Linux」のチェックボックスをONにする
- 今すぐ再起動で再起動する
- トイレタイム
- Microsoft Storeを開く
- 検索に「Ubuntu」と入力
- [入手]をクリック
- ダウンロードするのでティーブレイク
- ダウンロードが完了したら、[起動]をクリック
- Ubuntuが起動するが、初回はしばらく時間がかかるのでまたトイレタイム
- ユーザー名とパスワード2回を入力
- ここで、「サポートされていないコンソール設定です。この機能を使用するには、従来のコンソールを無効にする必要があります。」というエラーが出たら、コマンドプロンプトのプロパティ、オプションから、「従来のコンソールを使う」チェックをOFFにする、このとき再起動が必要になって、WSLがセットアップの中断となりおかしくなるので、Ubuntuアプリを削除して入れなおすと上手くいった

# nodo.jsをWSLに入れる(最新にする)
- まず、node.jsとnpmを入れる

```
sudo apt-get install -y nodejs npm
```

- 次に n package を導入

```
sudo npm cache clean
sudo npm install n -g
```

- さらに、n package を使って node をinstall

```
sudo n stable
sudo ln -sf /usr/local/bin/node /usr/bin/node
```

- バージョン確認

```
node -v
v10.6.0
```

- 最初に入れた nodejs, npm は削除

```
sudo apt-get purge -y nodejs npm
```

# cronで毎分起動するnode.jpツールで Google Drive Spreadsheetから情報を取得する


- 前提モジュールをインストール

```
sudo npm child_process sleep google-spreadsheet 
```

- googleの[デベロッパーコンソール](https://console.developers.google.com/flows/enableapi?apiid=sheets.googleapis.com)に行って、[プロジェクトを作成]を選ぶ
- [同意して続行]をクリック
- [認証情報に進む]をクリック
- [キャンセル]をクリック
- 「認証情報」のタブを選択し、 [認証情報を作成]をクリック
- 「サービスアカウントキー」をクリック
- サービスアカウントを作成
- KeyTypeはjsonを選択
- jsonを保存
- jsonの中の、"client_email"を調べておく
- [Googleスプレッドシート](https://docs.google.com/spreadsheets)に行く
- [Any new email]と言う名前のスプレッドシートを作る
- urlから、スプレッドシートのIDを調べておく(https://docs.google.com/spreadsheets/d/スプレッドシートのID/edit#gid=0となっている)
- A1にdate、B1にemail、C1にtitle、D1にnameと入力する
- [共有]をクリック
- 「ユーザー」に、上で調べた"client_email"を追加する
- 上のjsonを、WSLの適当なディレクトリに置く
- jsonを置いたディレクトリに以下のスクリプトを作成

```
vi gmail_watcher.js
```

- 中身はこんな感じ

``` gmail_watcher.js
const execSync = require('child_process').execSync;
var sleep = require('sleep');
var GoogleSpreadsheet = require('google-spreadsheet');
var id = 'スプレッドシートのID'
var path = 'フルパスのjsonファイル'
var my_sheet = new GoogleSpreadsheet(id);
var credentials = require(path);

my_sheet.useServiceAccountAuth(credentials, function(err){
    my_sheet.getInfo(function(err, data){
        data.worksheets[0].getRows(function( err, rows ) {
                for(var i in rows) {
                        var name = rows[i].name;
                        var title = rows[i].title;
                        var text = name+'さんから'+title+'と言うメールが来ています';
                        console.log(text);
                        text = text.replace(/\'/g, '');
                        text = text.replace(/\&/g, '');
                        execSync("curl -X POST -d 'text="+text+"' http://localhost:8080/google-home-notifier");
                        rows[i].del()
                        sleep.sleep(10);
                }
        });
    });
});
```
- 最後にSleep 10秒は、待たないと、1分間に複数メールが来た時に、台詞が消えてしまうのでアドホックに対応したもの。
- node.jsのフルパスを調べる

```
which node
```

- cron用のファイルを編集する

```
vi ~/crontab  
```

- 中身はこんな感じ。nodeとスクリプトのフルパスは適切に変更してください

``` crontab
*/1 * * * * /usr/local/bin/node  /home/google_home/gmail_watcher.js >> /var/log/cron.log
```

- cronに登録する

```
crontab < ~/crontab 
```

- cronが起動しているか調べる

```
sudo /etc/init.d/cron status
```

- 起動していれば、念のため再起動

```
sudo /etc/init.d/cron restart
```

- 起動していなければ起動する

```
sudo /etc/init.d/cron start
```

- 念のためにシステム起動時にcronが動くようにしておく

```
 sudo systemctl enable cron
```

- 念には念を入れて、cron.serviceがenabledになっているか調べる

```
systemctl list-unit-files -t service|grep cron
```

- 問題がなければ次へ

# google-home-notifierを入れる

- 前提モジュールをインストール

```
sudo npm google-home-notifier
```

# example.jsを編集する
- もとのexample.jsはいじりたくないので、コピーしたものを編集する

```
cd node_modules/google-home-notifier/
cp example.js myexample.js
vi myexample.js
```

- deviceNameを自分のGoogle homeのにする
- googlehome.deviceとgooglehome.accentを'ja'にする
- googlehome.ipを、googole homeアプリの右上の[デバイスアイコン]-そのgoogle homeの[:]-[設定]の一番下のIPアドレスにする。

``` node_modules/google-home-notifier/myexample.js
var express = require('express');
var googlehome = require('./google-home-notifier');
var ngrok = require('ngrok');
var bodyParser = require('body-parser');
var app = express();
const serverPort = 8080;

var deviceName = 'ダイニング ルーム';
googlehome.device(deviceName, 'ja');
googlehome.ip('192.168.xxxx.xxx');
googlehome.accent('ja'); // uncomment for british voice

var urlencodedParser = bodyParser.urlencoded({ extended: false });

app.post('/google-home-notifier', urlencodedParser, function (req, res) {
  if (!req.body) return res.sendStatus(400)
  console.log(req.body);
  var text = req.body.text;
  if (text){
    try {
      googlehome.notify(text, function(notifyRes) {
        console.log(notifyRes);
        res.send(deviceName + ' will say: ' + text + '\n');
      });
    } catch(err) {
      console.log(err);
      res.sendStatus(500);
      res.send(err);
    }
  }else{
    res.send('Please POST "text=Hello Google Home"');
  }

})

app.listen(serverPort, function () {
  ngrok.connect(serverPort, function (err, url) {
    console.log('POST "text=Hello Google Home" to:');
    console.log('    http://localhost:' + serverPort + '/google-home-notifier');
    console.log('    ' +url + '/google-home-notifier');
    console.log('example:');
    console.log('curl -X POST -d "text=Hello Google Home" ' + url + '/google-home-notifier');
  });
})
```

- テストする
- まず必要なサービスを起動する

```
sudo service dbus start
sudo service avahi-daemon start
sudo systemctl enable dbus
sudo systemctl enable avahi-daemon
```

- サーバプログラムを起動する

```
node myexample.js &
```

- 以下のようなメッセージが出る

```
POST "text=Hello Google Home" to:
    http://localhost:8080/google-home-notifier
    undefined/google-home-notifier
example:
curl -X POST -d "text=Hello Google Home" undefined/google-home-notifier
```

- 折角なので、日本語をしゃべらせる

```
curl -X POST -d "text=こんにちはGoogle Home" http://localhost:8080/google-home-notifier
```

- サーバの後始末をしておく

```
sudo ps -elf | grep myexample
```

- 上記コマンドでプロセスIDを調べてkillコマンドで停止させる

# nohupで動かしておく

- サーバはターミナルが停止しても動いてほしいので、nohup & で起動しておく
- 通常メッセージとエラーメッセージはファイルに吐くようにしておく

```
nohup /usr/local/bin/node /home/ubuntu/node_modules/google-home-notifier/myexample.js >> ~/out.log 2>> ~/err.log &
```

# 長かったけど以上！
- これで、Gmailが着信したらGoogle homeがしゃべってくれるはずです

# 追記
- メールタイトルや差出人の名前にアポストロフィーが含まれているとcurlがコケる問題を修正するため、`gmail_watcher.js`に`text = text.replace(/\'/g, '');`を追加した。