マストドンのHome TimelineをGoogle Homeにしゃべらせる

## はじめに

Google Home miniを半額で手に入れたので、何か使ってやろうと思い、しゃべらせてみることにしました。もうすでに誰かがやっている気もしますが、Mastodonのタイムラインの内容をしゃべらせてみます。

## 環境

* Ubuntu Server 17.10
* Node.js

Debian系のディストリビューションなので利用しました。Raspberry Piとかも同じDebian系なので同じようにできるのではと思います。

## Google Homeにしゃべらせる

[google-home-notifier](https://github.com/noelportugal/google-home-notifier)というライブラリがすでにあるので、使いました。
非常に簡単に使えます。

### google-home-notifier インストール

導入はgoogle-home-notifierのREADMEに書いてあるとおりですが、まずNode.jsと必要なライブラリをインストールします。

```shell
$ sudo apt-get install nodejs npm git-core libnss-mdns libavahi-compat-libdnssd-dev
```

その後、npmを使ってインストールします。

```shell
$ npm install google-home-notifier
```

READMEの [After npm install](https://github.com/noelportugal/google-home-notifier#after-npm-install) に書かれている通り、一部修正します。

node_modules/mdns/lib/browser.js というファイルの

```javascript
Browser.defaultResolverSequence = [
  rst.DNSServiceResolve(), 'DNSServiceGetAddrInfo' in dns_sd ? rst.DNSServiceGetAddrInfo() : rst.getaddrinfo()
, rst.makeAddressesUnique()
];
```

この部分を、下記のように書き換えます。

```javascript
Browser.defaultResolverSequence = [
  rst.DNSServiceResolve(), 'DNSServiceGetAddrInfo' in dns_sd ? rst.DNSServiceGetAddrInfo() : rst.getaddrinfo({families:[4]})
, rst.makeAddressesUnique()
];
```

### google-home-notifier の利用

使うのはしゃべらせる機能だけなので、こんな感じです。

```javascript
const googlehome = require('google-home-notifier');

var m = 'しゃべらせる内容';

googlehome.device('Google-Home','ja');
googlehome.notify(m);
```
家のGoogle Homeは「Google-Home」で始まる機器名だったようなので、上記でデバイスを検索できています。

実行すると、通知音の後、mに入れた文字列をしゃべってくれます。

## MastodonのHome Timelineの取得

StreamingAPIがあるようなので、それを使います。
まず、WebSocketを利用できるようにします。

```shell
npm install websocket
```

次に、Mastodonにクライアントの登録、およびアクセストークンを取得します。こちらはすでに他の方の優秀な記事がWeb上にあるので、見てください。

アクセストークンを取得したら、WebSocketで接続します。トゥートが来たらmessageの受信が行われます。eventプロパティが'update'のものが新規トゥートです。

```javascript
const WebSocket = require('websocket').w3cwebsocket;
const access_token = '取得したアクセストークン';
const host = 'Mastodonのホスト名。例えばmstdn.jpなど';

var ws = new WebSocket('wss://' + host + '/api/v1/streaming?access_token=' + access_token + '&stream=user');
ws.onmessage = (message) => {
    var d = eval('(' + message.data + ')');
    var p = eval('(' + d.payload + ')');
    if (d.event == 'update') {
        // p.contentがトゥートの内容
    }
}
```

### HTMLタグ除去

p.contentはHTMLのフラグメントとなっているようなので、しゃべらせるにはHTMLタグは不要です。[striptags](https://www.npmjs.com/package/striptags)なるライブラリがあるので、それを使って除去したりすると良いです。

```shell
$ npm install striptags
```

```javascript
const striptags = require('striptags');

// p.contentに対してstriptags()を実行する
```

## 組み合わせる
後は、MastodonのHome Timelineのトゥートを受信した際に、Google Homeにしゃべる内容を伝えれば良いです。

```javascript
ws.onmessage = (message) => {
    var d = eval('(' + message.data + ')');
    var p = eval('(' + d.payload + ')');
    if (d.event == 'update') {
        var m = striptags(p.content);
        googlehome.notify(m);
    }
}
```

## まとめ
これまでのプログラムをa.jsとかに保存して、Node.jsから実行すると、MastodonにWebSocketで接続して、Home Timelineに対するトゥートがあるまで待ちます。トゥートが来たらgoogle-home-notifierを利用してGoogle Homeがしゃべります。

```javascript:a.js
const googlehome = require('google-home-notifier');
const WebSocket = require('websocket').w3cwebsocket;
const striptags = require('striptags');
const access_token = '取得したアクセストークン';
const host = 'Mastodonのホスト名。例えばmstdn.jpなど';

var ws = new WebSocket('wss://' + host + '/api/v1/streaming?access_token=' + access_token + '&stream=user');
ws.onmessage = (message) => {
    var d = eval('(' + message.data + ')');
    var p = eval('(' + d.payload + ')');
    if (d.event == 'update') {
        var m = striptags(p.content);
        googlehome.notify(m);
    }
}
```

```shell
$ node a
```

## 応用

ws.onmessage の中で googlehome.notify を呼ぶための条件をつければ、特定の内容を含むトゥートのときだけ通知する、ということもできます。