Node.jsでWordPressのRSSを(なるべく)リアルタイムで持ってくるお話

> [プロ生ちゃん Advent Calendar 2018](https://adventar.org/calendars/3280) 21日目の記事、担当は音色ねいろです。

本来はもっとプロ生ちゃん色の強い何かを作ろうとしましたが、どうもこうもアイデアも出ず記事を書く時間も見つからずで、結果的にこの記事になりました。来年のネタを早めに温めておきたい。
今回の記事では、Node.jsを使用してWordPressのFeedをなるべくリアルタイムに持ってくる内容となっています。申し訳程度に[プログラミング生放送のフィードURI (https://pronama.jp/feed)](https://pronama.jp/feed)を例に取っています。

# きっかけ

# 要件(今回やること)
- Node.jsで(なるべくリアルタイムに)更新を検知する
    - この際相手のサーバに過大な負荷をかけることは許されないため、最小でも1分間隔で取る
- モジュール化する
    - **依存しないように取得部分と表示部分とを`EventEmitter`で分ける。**

# 今回使用したライブラリ
- data-utils
- iconv-lite
- xml2js
- sync-request

# 実装
```js:wpFeed.js
const EventEmitter = require('events').EventEmitter /* 1 */

require('date-utils')
const iconv = require('iconv-lite')
const xml2js = require('xml2js')
const request = require('sync-request')

let tempPostID = 0
let startupTime = '0'

module.exports = class wpFeed extends EventEmitter { /* 1 */

  constructor() {
    super()

    startupTime = new Date() /* 2 */
  }

  start() {
    const self = this

    setInterval(() => self._wordpress(), 60 * 1000) /* 3 */
  }

  _wordpress() {
    const res = request(
      'GET',
      'http://pronama.jp/feed'
    ) /* 4 */
    const buf = Buffer.from(res.getBody())
    const feedXml = iconv.decode(buf, 'UTF8') 

    xml2js.parseString(feedXml, (err, result) => { /* 5 */
      if(err !== null) {
        console.error(err)
      }

      const postData = result.rss.channel[0].item[0]
      const postID = postData['post-id'][0]._
      const pubDate = postData.pubDate
      const pubDateObject = new Date(pubDate)

      if(pubDateObject.getTime() < startupTime.getTime()) return /* 2 */
      if(postID === this.tempPostID) return /* 6 */

      this.emit('post', postData) /* 7 */
      this.tempPostID = postID /* 6 */
      
    })
  }
}
```
```js:app.js
const wpFeed = require('./wpFeed')
const wf = new wpFeed()

wf.on('start', () => {})
wf.on('post', data => {
  console.log(`記事が更新されました！\n「${data.title[0]}」`)
})

wf.start()

```
※実際に使用する際は、各処理を別ファイルに分離することをおすすめします。
## 1 : EventEmitterを継承する
今回の記事のキモです。
イベントモジュールのEventEmitterオブジェクトを継承しています。

## 2 : 取得開始前の記事をスキップする
これをしないと取得開始する度に最新の記事が取得されてしまい非常に厄介です。
そのため、コンストラクタ時に`startupTime`を格納し、記事取得時の時間と比較することによって取得開始後の記事のみをエミットすることができます。

## 3 : setIntervalで繰り返す
下に定義した`wpFeed`を120秒間隔で実行します。
もっとまともな書き方があれば教えていただきたいです…！

## 4 : フィード情報を取得してくる
現在sync-requestが非推奨になっていますが、同期的に処理したいためこれを利用しています。
まともな書き方があれば(以下略)

## 5 : XMLからJSONにコンバートする
XMLだと非常に扱いにくいので、JavaScriptのために生まれてきたJSONにコンバートします。
XMLに誤りがあると変換がうまくいかない場合があるので、必ずエラー処理を噛ませておきます。

## 6 : 重複スキップ
直前に取得した記事IDと現在処理中の記事IDとを比較します。
比較した際、直前のIDと一致した場合は早期リターンを用います。
ただ、内容が変更された場合は感知できないようなので比較対象を内容にするべきですね。

## 7 : エミット
更新された記事をエミットします。
こうすると、`app.js`側の`wpFeed.on('post', postData => {})`にデータが飛び、自由自在にデータを利用することができます。

# まとめ
去年の今頃からNode.js書き始めましたが、「こいつ動くんか？」といったコードも(それなりに綺麗なコードで)すんなり動いてくれてとても好きです。

今回、EventEmitterを利用しましたが、本当に様々なことに使えるので大好きです。([ep8](https://github.com/neirowork/ep8)もデータの取得にEventEmitterを使用しています)

# 今後やりたいこと
- 更新されたらLEDがチカチカするIoTしたい
- クラス生成時にURL指定、更新間隔を変更できるようにしたい
- npmで配布したい
- TypeScriptで書いてみたい

# 最後に
本当に昨日(20日)まで何を書こうか全く決まらなくて、結果今年学習した内容の記事になりました。
来年こそはもっとプロ生ちゃんにコミットしたいです……！
ここまでご覧頂きまして、本当にありがとうございました。

[プロ生ちゃん Advent Calendar 2018](https://adventar.org/calendars/3280) 22日担当は120byteさんです。