Twitterから効率よく情報収集できるかもしれない「node-twatch」というCLIツールをNode.jsでつくりました

Twitterで有益な情報を発信する人のツイートを監視して、そのツイートの中でも特に自分が欲している情報だけを頂戴しようというゲスいツールをつくりました。

その名も__「twatch（トゥウォッチ）」__です。
Twitter+watchの造語となっております。

https://github.com/kokushin/node-twatch

## 概要

ざっくりいうと、__フォローしているユーザーのツイートをリアルタイムで監視し、ツイートが投稿されたらSlackへ転送してくれるコマンドラインツール__です。詳細はGitHubの[README.md](https://github.com/kokushin/node-twatch/blob/master/README.md)をご確認くださいませ。

オプションとして、

- 特定のユーザー名（スクリーンネーム）のツイートのみ監視（カンマ区切りで複数指定可能）
- 特定のキーワードを含むツイートのみ監視（カンマ区切りで複数指定可能）
- リンクを含むツイートのみ監視

の3種類のフィルターが用意されています。
これらは組み合わせて使用することができます。

↓ （動作デモ）「@lqxia」が「node.js」というキーワード含むツイートを行った際に、Slackへツイートが転送されている

![demo.gif](https://qiita-image-store.s3.amazonaws.com/0/57867/0052ef9a-5422-909d-3bde-b8fc4e1467aa.gif)

Slackからプッシュ通知が飛ぶので気になればすぐにチラ見もスルーもできますし、ログも残るので後から読み返せます。
Twitterのようにずーーっと画面を開いて監視することも、ツイートの山に埋もれて探せない...なんてことも減るはずです。（たぶん）

## 開発時のメモ

実はNode.jsでCLIツールを作るのは初の試みでして、色々と試行錯誤しながらすすめました。
開発中に得られた知識をピックアップしてご紹介します。

### Node.jsは6.11.2を使用

6x系からES6が安定して使えるため、2017年9月時点で最新の推奨版である6.11.2で開発しました。

### ツイートの取得には Twitter Streaming API を利用

ツイートをリアルタイムで取得する必要があったので、「Twitter Streaming API」を利用しました。
npmで提供されている[twitter](https://www.npmjs.com/package/twitter)を使うことで、簡単に実装することができました。

### Twitter Streaming API の 'statuses/filter' ではなく 'user' を使う

Twitter Streaming APIで取得するデータに対して、「ユーザー名、キーワード、言語」などフィルターを設定して取得することができる__'statuses/filter'__というものが存在しており、これを使えば楽勝かなーと進めていたらユーザー名の指定のところでエラー。

```js
client.stream('statuses/filter',
  {
    track: 'javascript',
    language: 'ja',
    follow: 'kokushing', // エラー！
    ...
  }
...
```

いろいろ調べていたら、どうやらTwitterのユーザー名（スクリーンネーム）を指定するのはNGで、数字だけで構成された裏IDのようなものを指定しないといけないらしいです。このIDをいちいち調べてコマンド入力するのも面倒だな〜ってのとそんなに大量の情報も必要ないので、'statuses/filter'を使ったフィルタリングは断念し、__'user'__というものとif分岐を組み合わせて処理することにしました。

'user'を指定すると、__「フォローしている全ユーザーのツイートのみを取得」__することができるようです。

なので、フォローしているユーザーに限られてしまいますが、下記のようにif分岐と組み合わせることでとりあえず意図していた動作に持っていくことができました。

```js
client.stream('user', {}, (stream) => {
  stream.on('data', (obj) => {

    // 取得したツイートのオブジェクト内に含まれるユーザー名、キーワードを
    // 入力されたオプションの条件と一致するかどうか正規表現で判別し、
    // 一致したらSlackに転送する
    let tweet = {
      'username': obj.user.screen_name, // Twitterのユーザー名
      'text': obj.text, // 投稿されたツイート内容（URL含む）
      'checkText': obj.text.replace(/(https:\/\/t.co\/([a-z|A-Z|0-9]+))/g, ''), // URLを含めないツイート内容（if分岐用）
      'checkLink': obj.entities.urls.length > 0, // ツイート内容にリンクが1つ以上含まれているか（if分岐用）
    }

    // 1. ユーザー名、キーワードが入力されたオプションと一致しているかどうか
    if (username.test(tweet.username) && keyword.test(tweet.checkText)) {

      // 2. リンクを含むツイートのみ監視:ONのときにリンクが含まれていないものは転送させない
      if (options.link === true && !tweet.checkLink) {
        return
      }

      // 3. すべての条件を満たしたツイートのみをSlackへ転送
      sendMessage(tweet.username, tweet.text)
    }
...
```

### 入力されたコマンドの引数をNode.jsで受け取るには minimist を使う

コマンドの引数は `process.argv` に格納されるのですが、 [minimist](https://www.npmjs.com/package/minimist)を利用すると引数の取得周りの処理がちょっとラクになります。

```js
const options = {
  user: minimist(process.argv).u || minimist(process.argv).user,
}
```
上記の場合だと、`-u` または `-user` という引数が存在する場合はその内容取得しを文字列で `options.user` に格納します。
引数が存在しない場合は `undefined` が返され、引数は存在するが中身がない場合は `true` を返します。

### ターミナル上で表示される情報をデザインする

せっかくのCLIツールなので、出力テキストを着色することができる[chalk](https://www.npmjs.com/package/chalk)、アスキーアートを生成できる[figlet](https://www.npmjs.com/package/figlet)、ローディングスピナーを生成できる[ora](https://www.npmjs.com/package/ora)なんかを使ってみました。
特にアスキーアートを生成できる「figlet」はギークな雰囲気を醸し出せるのでオススメです。

![Screen Shot 2017-09-04 at 17.05.08.png](https://qiita-image-store.s3.amazonaws.com/0/57867/de6fa9b0-f3bd-732d-eecd-55beb56049c8.png)

### package.jsonに実行するコマンドを記述する

CLIツールなので、スクリプトを実行するためのコマンドとスクリプトをpackage.jsonへ登録します。
ローカルで実行される場合とグローバルで実行される場合の2パターンあります。

ローカル（$ npm run twatch）で実行されることを想定したコマンドとスクリプトを__"scripts"__へ記述します。
※npm run scriptを利用することで自動的にパスが通ります

```json
"scripts": {
  "twatch": "node app.js"
}
```
グローバル（$ twatch）で実行されることを想定したコマンドとスクリプトを__"bin"__へ記述します。

```json
"bin": {
  "twatch": "app.js"
}
```

### npmへの登録はnpで！

ソースコードをGitHubで管理している必要がありますが、npmへ登録するときは[np](https://www.npmjs.com/package/np)というパッケージを利用するとラクです。
GitHubのタグの更新、リリース用ZIPファイルの作成と、npmへのアップロードを一括で実行してくれます。

## 実際に使ってみて

まだ試験運用中なんですが、結局のところTwitterフォローするユーザーの選定とキーワードの選定が難しいという感じです。
今は著名人、Web技術ネタをよく発信してくれる人、Qiita新着・人気、はてブホットエントリーなんかをフォローしてます。

キーワードは `html,css,js,javascript,web,node,npm,yarn,gulp,php,react,vue,design,git,ux,ui,google,iphone,android,ios,mac` こんな感じです。まだまだ改善の余地がありそうですね...

<img width="689" alt="Screen Shot 2017-09-04 at 20.25.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/57867/f969db82-7bec-51d1-f3d9-a9e616123bb2.png">

程よく拾えてるような気もします...
キーワードやフォロワーを定期的に変えてみて、ベストなパターンを研究中していこうと思います。

あと、現時点のバージョンでフォローしている人からしかツイートを取得できませんが（個人的には十分なのですが）、次のアップデートで全Twitterユーザーを対象にできるようなオプションを追加しようと思っています。
キーワードでフィルター掛けないととんでもない量の通知が飛びそう...

## おまけ Raspberry Pi で常時起動させてみた

Node.js 6.11.2をインストール。
オートログインを設定して、`/home/pi/.bash_profile` の中にコマンド書くだけで出来ました。

![asset.PNG](https://qiita-image-store.s3.amazonaws.com/0/57867/0a35c082-6e21-24c7-6743-842b3897bdcb.png)

熱とか怖いですが、今のところ元気に動いてます。

## 参考にさせていただいたページ

- [特定のワードで Twitter を監視して、検知したら Slack に投げる](http://b4b4r07.hatenadiary.com/entry/2016/10/17/205021)
- [Twitter Streaming APIを使って特定のハッシュタグが付いたツイートを即リツイートする](http://y6rasaki.hatenablog.com/entry/2017/04/07/005508)
- [Twitter Developer Documentation](https://dev.twitter.com/streaming/overview)
- [第三回 Raspberry Pi 3に最新のNode.jsをインストールする](http://qiita.com/setouchi/items/437e4b62e4210871496f)