0

hubot-rss-reader v0.8.1をリリースした

hubot-rss-readerをv0.8.1にアップデートした。

このアップデートでは、hubotが起動していない期間の記事を正しく配信できるようになった。Heroku無料枠で1日18時間制限がある場合などに便利。

結論から言うと、hubot-rss-reader v0.8.x以降はHubotに最初から入っているredis-brainをhubot-mongodb-brainなどのまともな実装に切り替えないと300日ぐらい後に死んでしまう可能性がある。



何が変わったか

RSSから取得した記事のうち、何が新着で何がチャットに配信済みなのか、という判定方法を変更した。


これまでの実装

新着記事を取得したらそのURLをオンメモリに持っておいて、RSSチェック毎に新しい記事かどうか判定するのに使っていた。つまり起動して最初の1回目のRSSチェックは配信済みURLリストとして保存されるだけになっていた。

なぜディスクに保存していなかったかというと、Hubotデフォルトのredis-brainの実装があまりにもひどかったから。詳しくは下に書いた
hubotのbrainが爆発した
またHubotのbrainが爆発したのでhubot-mongodb-brain作った

redis-brainがひどい

redis-brainは全データまとめてJSON.stringifyして1つkeyに巨大な文字列として保存している。しかも更新が無くても20秒毎にRedisに書き込んでいる。
サービスによって設定値が違うだろうけど、例えばRedisToGoでは1回に1.5MBを超えるとmaxmemory errorが起きて失敗する。もし毎日50byteのURLを100個保存すると1日で5Kbyteになり、300日で1.5MBを超えてしまい、死ぬ。

そもそもbrainはKVS風なAPIなのになぜRedisをKVSとして扱わないのかよくわからない。

俺は自作のhubot-mongodb-brainに切り替えているので問題ないが、ふつうのredis-brainではURLリストごときでもヤバイので、保存する事ができなかった。


v0.8.0での変更


Herokuが無料プランでは1日18時間しか動かせなくなったので、夜に6時間寝るように設定している
するとオンメモリに持っていたURLリストは消えるので、夜のうちに更新された記事は新着/既出判定できず、配信できなくなっていた。

これはかなり困る。
300日で破裂するbrainとどっちを取るかっていうとRSSがちゃんと配信されない事の方が嫌だったので、brainに保存するように変更した。


まとめ

brainを変更したほうがいい

0

Node.jsのfeedparserにドイツ語等を読ませる

ドイツ語などのヨーロッパの言語ではたまにåとかöみたいな文字が使われていて、UTF-8とかじゃなくISO-8859-1などの文字コードが使われている事があるらしい。

ドイツのspiegelという雑誌のRSSがちょうどISO-8859-1で、hubot-rss-readerで読むとところどころ文字化けするようになっていた。

こんな感じ


Node.jsでfeedを読む

feedの取得にはrequest、parseにfeedparserを使うのが多分普通なのだが、この2つはpipeでつないで使うようになっている。

で、最初は
Node.jsで文字コードの自動判別と自動変換 – Qiita
のようにjschardetで文字コードを判別して、iconvで変換してみた。でもrequest→feedparserがstreamなので少しずつchunkで送られてきて、日本語のマルチバイト文字の途中で区切りが来たりする事がある。そのchunkを1つずつjschardetにかけてしまうと全然違う文字コードが出てきたりする事があって良くない。

冷製になって考えたら、XMLなんだから頭に<?xml encoding=”ISO-8859-1″とか書いてあるのを読んでUTF-8じゃなかったら変換するstreamを作ればいい事に気づいた。

streamでXMLを食わせるとUTF-8に変換して吐き出すstream

というわけで、まずXMLのencoding attributeを読んで必要があればUTF-8に変換するstreamを返す関数を作る

charset-convert-stream.coffee
stream = require 'stream'
Iconv = require('iconv').Iconv

module.exports = ->

iconv = null

charsetConvertStream = stream.Transform()

charsetConvertStream._transform = (chunk, enc, next) ->
if m = chunk.toString().match /<\?xml[^>]* encoding=['"]([^'"]+)['"]/
charset = m[1]
if charset.toUpperCase() isnt 'UTF-8'
iconv = new Iconv charset, 'UTF-8//TRANSLIT//IGNORE'
if iconv?
@push iconv.convert(chunk)
else
@push chunk
next()

return charsetConvertStream


ドイツ語のfeedを読む

requestのon “response”からpipeでcharsetConvertStreamを通してfeedparserに繋ぐと、いい感じにUTF-8にエンコードされて出てくる。

FeedParser = require 'feedparser'
request = require 'request'
charsetConvertStream = require './charset-convert-stream'

feedparser = new FeedParser

req = request
uri: process.argv[2] or 'http://www.spiegel.de/schlagzeilen/tops/index.rss'
timeout: 10000
encoding: null # null指定でrequestが勝手にエンコードしなくなる
headers:
'User-Agent': 'test-feed-reader'

req.on 'error', (err) ->
console.error err

req.on 'response', (res) ->
if res.statusCode isnt 200
return console.error "statusCode: #{res.statusCode}"
this
.pipe charsetConvertStream()
.pipe feedparser

feedparser.on 'error', (err) ->
console.error err

feedparser.on 'data', (entry) -> # エントリーが1件ずつイベントで出てくる
console.log entry.title
console.log entry.summary or entry.description


文字化けなくなった

ここまでやって気づいたんだけど、 feedparser.meta[‘#xml’].encoding にエンコード情報が入っているのでこれを使ってtitleやdescriptionなど必要な所だけ取り出してからiconv.convertしても良かったかも・・

0

hubot-rss-readerの本文を見やすくした

hubot-rss-reader

hubot-rss-reader

チャットチャンネルを個別にRSSリーダーにできるhubotスクリプト。
% npm install hubot-rss-reader
でインストールできる。

本文を見やすくした


今までsummaryを表示していたけど、githubのRSS等はsummaryが無くてdescriptionだけある。どちらかある方を表示するようにした。

summaryがHTMLで書かれている場合は、HTMLタグを除去した。

一番最初のimgタグの中身を記事の先頭に持ってくるようにした。slackなど画像の自動ロードが有効なチャット環境の場合、ヘッダ画像のようになって見やすい。

無駄な改行を除去した。3つ以上改行のみの行が続く場合、改行2つにまとめた。

スクリーンショット



1

Hubotで読めるRSSリーダー作った

チャットのチャンネル(部屋)毎にRSSを登録して、RSSリーダーにできるhubot scriptを作った。

https://github.com/shokai/hubot-rss-reader

去年nikezonoが「人の読んでるRSS読みたい、むしろRSSのリスト作ってお前コレ読めって薦めたり、他人にRSSの購読を追加してもらったりしたい」というような事を言ってた気がしたので、最近hubotが楽しいし作った。


こんなの


チャットはSlack.comで、HubotはHerokuに置いてつないでる。

「hubot rss add (URL)」とコマンドを発言すると部屋にRSSを追加できるようになっていて、チャットルームごとにRSSが購読できる。

Slackではプロジェクト毎に部屋を作ってGithubやTravisの通知を流すように運用している。

  • そのプロジェクトに関連あるリポジトリのRSSを流す
  • 研究室全員のgithubのfeedを流す部屋を作っておいて、個人活動を共有する
  • 技術系のblogを誰でも何でも登録していいよという部屋を作っておく

とかすると便利だと思う。若者のRSS離れを抑止できる。


インストール


https://www.npmjs.org/package/hubot-rss-reader

npmでhubotにインストールして

% npm i hubot-rss-reader --save

external-scripts.json に
["hubot-rss-reader"]
を追加すれば使える。


環境変数で色々設定できるが、しなくても適当に動く。
export HUBOT_RSS_INTERVAL=600      # 600秒ごとに更新チェック
export HUBOT_RSS_HEADER=:sushi: # RSS Header Emoji (デフォルトで寿司)
export DEBUG=hubot-rss-reader # debug printを有効にする


なおチャットルーム毎のfeedはredisに保存するようになっている。
hubotはデフォルトでredisを使うので、ふつうにredis-server起動しておけば特に必要ない。
herokuなら
% heroku addons:add redistogo:nano
でok


コマンド


追加と削除とリストしか今のところ無い。

hubot rss add https://github.com/shokai.atom
hubot delete add https://github.com/shokai.atom
hubot rss list

0

MakezineのRSS全文

jpとcomそれぞれ、全文配信されてなくて記事読むのがクソ面倒臭かったのでpipes作った

Pipes: Make: fullfeed

Pipes: Make: Japan fullfeed