mikutterの最新の情報は、mikutter blogに引っ越しました。

2011年1月15日土曜日

自動投稿(Postクラス)

Twitterクライアントの基本はつぶやくこととつぶやきを見ること。mikutterでは、Post#update を使ってつぶやけます。Post#postというエイリアスもあります。

Postクラス http://mikutter.hachune.net/rdoc/classes/Post.html

でも、updateは内部ではdefine_methodを使って定義しています。だからRDocには載っていません。載せ方書いてあったけどうまくいかなかった
このPostオブジェクトはもともとつぶやきの投稿のためのものだったんだけど気づいたらAPIのインターフェイスになってました。だからfriend_timelineとかも実はこのクラスを使って取得するんだけど、普通はイベントを使ってください。そのうちServiceに名前変えたいと思ってたけどそろそろ引っ込みがつかなくなってきました

肝心のupdateの使い方ですが、引数はハッシュ1つで、symbolで最大3つの引数を与えられます。

  • :message つぶやきの本文をStringで。必須
  • :replyto リプライ先のメッセージ(in_reply_to_status)。Messageのインスタンスを渡す
  • :receiver リプライ先のユーザ(in_reply_to_user)。このユーザに宛てられる。本文に@そのユーザ名が含まれているかreplytoがセットされていれば自動的に補完される。Userのインスタンスで。

あと、実際の投稿は勝手に別スレッドでやります。気をつけてください。
ブロックを渡すと、(状態, 補足説明)を引数に取って以下のタイミングで呼び出されます。

  • (:try, str) コネクション開始。strは引数の:messageの値。
  • (:success, message) 投稿成功。messageは投稿したメッセージのMessageオブジェクト。
  • (:fail, exception) 失敗。これが渡ってきたらもうリトライしないということ。exceptionには発生した例外オブジェクトか、不正なHTTPコードが送られてきた場合はnilが渡される。
  • (:retry, exception) 失敗。ただし、後でもう一回接続を試みる(現在は1秒後に決め打ち)。

以下の例は、よるほーするプラグインです。

Module.new do
  plugin = Plugin.create(:yoruho)
  plugin.add_event(:boot){ |service|
    Thread.new{
      yoruho = nextyrhtime
      loop{
        nex = yoruho - Time.new # 次回のよるほーまでの秒数
        if(nex <= 0.5)
          say_yoruho(service)
          sleep(43200) # 半日待つ
          yoruho = nextyrhtime
        else
          sleep(nex / 2)
        end
      }
    }
  }

  # 次回のよるほー時間を取得
  def self.nextyrhtime
    now = Time.new
    result = Time.local(now.year, now.month, now.day, 0, 0)
    while result < now
      result += 86400 end
    result
  end

  # よるほーとつぶやく
  def self.say_yoruho(service)
    service.update(:message => 'よるほー'){ |stat, value|
      if stat == :success
        reflection(service, value)
      end
    }
  end

  # messageがよるほーに成功していたらドヤ顔をする
  def self.reflection(service, message)
    if is_pitari?(message[:created])
      service.update(:message => '決まったァ…(ドヤ')
    else
      service.update(:message => 'なん・・・だと・・・')
    end
  end

  # timeが0時0分0秒ならtrueを返す
  def self.is_pitari?(time)
    time.hour == 0 and time.min == 0 and time.sec == 0
  end

end

適当な実装だけど、だいたいこんなかんじ。時間の判定がやたら汚いけど、まあこんなふうにとっても簡単に実装できます。
ちなみにbootっていうイベントは起動時に一回だけ他のイベントより先に呼ばれるイベントで、今のところはPostオブジェクトを得る一番ベターな手段です。

リプライの送り方ですが、あるメッセージへの返信だったら、Message#postを使えばリプライが打てます。今回は省略。

つぶやきの受信と送信の機能があれば、よくあるお遊びbotは実装できます。例えばフォローしてる人限定ぼむったーなんかは簡単な正規表現だけで実装できます。がんばればおはなしbotなんかも実装できるでしょう。

2011年1月14日金曜日

PluginTag(プラグインオブジェクト)

mikutterの内部の話をするブログを作ってみた飽きたらやめる
基本的にRubyを知ってる人に対して書いてます。へんなことかいてたらごめん

プラグインの作成方法基本編です。

まず、mikutter.rbのあるディレクトリのplugin/以下に、なんとか.rbというファイルを作成します。すると起動時に勝手に読み込まれます。ここにrubyでゴリゴリプラグインを実装していきます。

一番キーになるのがPluginTagクラス。でも実際は、Plugin.createを使ってプラグインを作るので、PluginTagという名前はあまり使わないかも。詳しくはRDocをみてね。

http://mikutter.hachune.net/rdoc/classes/Plugin/PluginTag.html

まず、騙されたと思ってPluginTagのインスタンスを得ます。Plugin.createにはプラグインのファイル名の拡張子の前をSymbolで渡してあげればいいと思います。同じシンボルを渡せば同じインスタンスを返しますが、長ったらしいので変数に退避するのが望ましいです。
あと、関数のスコープが別のプラグインとぶつかるのが嫌なので、無名モジュールで囲ったりしてます

Module.new do
  plugin = Plugin.create(:myplugin)
end

mikutterの中では、TLにつぶやきが流れてきたりするたびに「イベント」が発生します。このイベントを投げる/受けることで、プラグインはコアや別のプラグインとコミュニケーションをとってるんです。
例えばフォローしてる人がつぶやいたらupdateイベントが呼ばれ、そのイベントをlistenしているプラグインが起動されると。
ごたくはともかく例。

Module.new do
  plugin = Plugin.create(:myplugin)
  plugin.add_event(:update){ |service, messages|
    messages.each{ |m|
      puts m.to_s
    }
  }
end

イベントをリスンするにはPluginTag#add_eventを使います。詳しい使い方はRDocにかいてあるけれど見たとおりで、updateイベントが発生するとブロックが実行されます(イベントのコールバック)。
イベントごとにブロックの引数が決まっています。それも上のリンク先に書いてありますが、updateイベントの場合はPostクラスとMessageクラスの配列を引数に与えられることになっています。
Messageクラスはつぶやき1件を表現するクラスなので、これだけでTLをターミナルに書き出すプラグインの出来上がりです!短い!
ひとつのプラグインにいくつでもイベントをリスンさせることもできます。リプライも表示させるならこんな感じ

Module.new do
  plugin = Plugin.create(:myplugin)
  plugin.add_event(:update){ |service, messages|
    messages.each{ |m|
      puts m.to_s
    }
  }
  plugin.add_event(:mention){ |service, messages|
    messages.each{ |m|
      puts m.to_s
    }
  }
end

ね、簡単でしょう?思いついたとおりに描いていけばいいんです。

大事なことは、イベントは必ずメインスレッドで実行されるということです。mikutterはメインスレッド以外でGtkを叩いてはいけないことになっています。だからイベントのコールバック内ならGtk関連の操作をしてもOK。ただし、あまりにも時間のかかる処理をここでしてしまうとmikutter全体のレスポンスが悪くなりますね

イベントの概念は、Twitterに例えたら、自分がPluginTagのインスタンスで、イベント(updateとか)が他人のアカウントで、add_eventとかでフォローする感じ。解りやすいかと思ったけど文字にしたら解り難かった。

まああれだGtkで言うところのシグナルですふつうに

まとめ

mikutterプラグインの根幹となる概念「イベント」についてちょっと触れました。短縮URLなど一部のプラグインを除けばほとんどのプラグインがこのイベントを使っています。
毎回こんな感じで書いていって、あとからマニュアルみたいに使えたらいいなってだったらちゃんとかけよおれ

きょうはなんかちょっと冷たいものが歯にしみたので歯をよく磨いて寝ます。時間も遅いから校正はやめとく おやすみ