会社のIRCで ircbot console を利用してbotを立てて、Jenkinsタスクの完了時にカスタマイザブルなメッセージを流したり、github enterpriseやfecruのRSSを加工して流したりしてたのですが、拡張性に限界を感じたので、今更ながらhubotとcoffeescriptを触ってみたよという話。
今回色々触った成果物はこちら github
IRCbot console でやっていたこととhubotでの実装法
↑すべてをとりあえずhubotでも実装出来ました。
hubotをIRCで使うための基本
hubotとともにhubot-ircを入れれば良いです。 こちら などが詳しいです。 検索すると結構noticeができなくて躓いてる人がいるようですが、hubot-ircのadapterでnoticeメソッドが定義されていたので、
robot.adapter.notice bot.envelope, "こんにちは!"
みたくすれば利用可能でした。ドキュメントをちゃんと読んでないので、これが開発者の意図した使い方かは不明ですが。
botに呼びかけるとしゃべる
これはhubotの基本機能、hearを使い、bot名で反応するようにすることでできます。
module.exports = (robot) -> robot.hear /mana_bot/, (bot) -> robot.send bot.envelope, "こんにちは!"
周期スクリプトで通知
nodeのモジュールにcronを追加し、enterでcron登録することで実現可能です。
cron = require('cron').CronJob module.exports = (robot) -> robot.enter -> new cron cronTime: "0 0 17 * * *" start: true timeZone: "Asia/Tokyo" onTick: -> robot.send { room: "#mana_bot" }, "もう5時か・・・小腹空いたなあ"
部屋指定のためにオブジェクトでっち上げてますが、これでhubot的に正しいのかは不明。とりあえず動作します。
RSSを監視してつぶやく
nodeのモジュールにrequest、feedparserを追加して ↑と同じくRSSを取りに行きたいタイミングでcron実行してやればよいです。 ただし、何もしないと同じエントリを何回もつぶやいてしまうので、つぶやいたかどうかはrobot.brainに保存しておいてあげます。
cron = require('cron').CronJob feedparser = require 'feedparser' request = require 'request' module.exports = (robot) -> watchFeed = (cronTime, url, callback) -> new cron cronTime: cronTime, start: true, timeZone: "Asia/Tokyo", onTick: -> entries = [] request(url) .pipe(new feedparser []) .on('error', console.log.bind console) .on('data', entries.push.bind entries) .on('end', -> lastEntries = {} entries.forEach (entry) -> lastEntries[entry.link] = true if robot.brain.data[url]? and not robot.brain.data[url][entry.link]? callback entry robot.brain.data[url] = lastEntries robot.brain.save() ) robot.enter -> watchFeed '0 * * * * *', "http://b.hatena.ne.jp/entrylist?sort=hot&threshold=3&mode=rss", (entry) -> console.log entry.title robot.send { room: "#mana_bot" }, "#{entry.title} #{entry.link}"
はてなの人気エントリを毎分取りに行って新しいものをつぶやかせる例です。
curl経由で様々なメッセージを流す
hubotは内部にhttpdも持っているので、それを利用してつぶやくようにしてあげればOKです。 環境によっては環境変数 PORT と HUBOT_HTTPDを設定してあげる必要があるかもしれません。
module.exports = (robot) -> robot.router.get "/send", (req, res) -> msg = req.query.msg ch = req.query.ch robot.send { room: ch }, msg res.end JSON.stringify {ch:ch,msg:msg}
/sendに対してチャンネル名とメッセージをGETで送ってあげると、それをしゃべります。
IRCbot consoleでできたことが大体できて、僕満足!
coffeescriptについて
こちらも今回はじめて触ったのですが(今更)、ブログ読んだりしてる限りはJavaScriptのシンプルさをそのままに記述を簡潔にし、よりスクリプティングに向いた言語というイメージでしたが、だいたいそんな感じでした。カッコ省略周りやクロージャの記述が柔軟なので、DSLっぽく利用するのに向いていると思います。
個人的にはオブジェクトリテラルの省略記法と関数適用のカッコ省略を合わせて、
new User name: "manaten" url: ""
みたく、名前付き引数っぽく記述できるのと、
on "click" -> console.log "click!"
として、関数に無名関数を引数に渡すのを、そういう構文っぽく記述できるのが好みです。 シンプルな記述を組み合わせて柔軟に表現できるとかそういうのが好き。
ただJavaScriptと比べて気になった点が幾つか。
- var文がないので、同名の変数をスコープの内外で定義できない
- カッコを省略できるからと言って、省略し過ぎると 特に .演算子などがおかしく適用されてしまう
このあたり、coffeeはJavaScriptにコンパイルされてから動くということもあり、ハマると結構はまってしまうので慣れるまで注意が必要だと思いました。