RubyからGitHubのpull requestをSkypeに通知する(てきとう版)

GitHubのビジネスプランを使ってるんですが、pull requestの通知をチームメンバーが使ってるSkypeチャットに送りたかったので、SkypeでのBotの作り方を調べてみました。
どうにも面倒そうな印象があったんですが、やっぱり結構面倒でしたw


Linux上のマシンでアプリケーションからSkypeにメッセージを送る際には、dbusを経由してコマンドを送る必要があります。
公式のドキュメントは以下。
Skype for Developers


rubydbusを使おうと調べてみたら、ruby-dbusというものがあったので、これを使おうとしたんですが、
Skypeを触るのにもっとダイレクトなrypeというライブラリを発見したのでそれを使うことにしました。
詳細はリンク先にあるので省きますが、要はdbusのラッパーですね。


で、こんな感じで書いてみました。
こんなのにaction_viewとか使ってるあたり超手抜き感が溢れてますが・・・。

require "rubygems"
require "bundler/setup"
require "action_view"
require "rype"
require "awesome_print"
require "simple-rss"
require "open-uri"
require "yaml"

class GithubSkypeNotifier
  include ActionView::Helpers::SanitizeHelper

  def initialize(atom_url, chat_id)
    @atom_url = atom_url
    @chat_id = chat_id
    Rype.attach

    Dir.chdir(File.dirname(__FILE__))

    unless File.exist?("notified_list.txt")
      File.open("notified_list.txt", "w") {|file| puts "Create notified_list.txt"}
    end

    notified_list = File.open("notified_list.txt", "r")
    @notified_items = notified_list.readlines.map{|l| l.chomp}.reduce({}) do |table, item_id|
      table[item_id] = true
      table
    end
    notified_list.close
  end

  def get_items
    SimpleRSS.parse(open(@atom_url)).items.select {|item| !@notified_items[item.id]}
  end

  def notify(item)
    message = <<EOM
==Github Activity Notify==
#{item.title}
#{item.link}
detail:
#{strip_tags(CGI.unescapeHTML(item.content)).gsub(/\s*\n/, "").gsub(/\s+/, " ")}
==========================
EOM
    Rype.chat(@chat_id).send_message(message)
    puts "send message following:\n#{message}"
    log_item(item)
  end

  def log_item(item)
    unless @notified_items[item.id]
      @notified_items[item.id] = true
      File.open("notified_list.txt", "a") {|file| file.write(item.id + "\n")}
    end
  end
end

def load_config
  config = YAML.load_file("config.yml")
  config
end

config = load_config

notifier = GithubSkypeNotifier.new(config["atom_url"], config["chat_id"])

notifier.get_items.each do |item|
  notifier.notify item
end

Gist版 → https://gist.github.com/2850118


面倒だったので、とりあえずActivityのAtom Feed読んで、送ってないitemが来たらSkypeに投げるって感じです。
余計なメッセージも来るけど、とりあえずやりたい事は出来た。


チャットにメッセージを送る時はチャットIDを調べておかなければいけないのですが、
一番調べやすいのは、送りたいチャット窓だけ開いておいて、以下のコードを実行することです。

Rype.attach
Rype::Api.invoke("SEARCH ACTIVECHATS") do |a|
  p a
end


チャットIDさえ分かれば、後は結構簡単に送信できます。
ただし、dbusを経由しているため、X関係の環境変数と、DBUS_SESSION_BUS_ADDRESSという環境変数を持っている必要があります。
(詳細は未確認)
X環境下のターミナルから呼ぶなら問題無いんですが、cronとかで動かそうとすると結構ハマります。
とりあえずここのサイトを参考に、シェルスクリプト環境変数の読み込み処理をかませて、rubyを呼ぶことで対処できました。


でも、再起動したらDBUS_SESSION_BUS_ADDRESS変わりそうなので、安定なボットを作るのはもうちょい工夫が必要ですね。
xvfbを使ってSkypeをヘッドレスで起動するテクニックもあるようですが、まだ試してません。
とりあえずは暫定策なので、これぐらい出来ればいいかなーと。
この辺上手いこといったら、Jenkinsに組込みたいんだけど、そんな暇なさそう。


チームでSkypeを使っていて、外部のイベントをプッシュ通知したい時には使えるかもしれません。


PythonにはSkype4Pyというメジャーなライブラリがあるらしいので、Python使いの人の方が楽そうです・・・。