……と書いたところで間違えて確定を押してしまった(爆
えーと、LeopardからのRubyCocoa環境がズバ抜けていいので、試しにソーシャルブックマークサービス用のツールをなんか一つでっち上げちまおうと思いたったわけです。……その前にやることやれよorz
最新のRSSParserが必要なので注意してください。
ダウンロード。
動かすと勝手にはてブにwww.example.orgを追加すると云う酷い仕様なので気をつけてください。
(if $0 == __FILE__ 〜 end を書き換えれば大丈夫)
既にありそうな気がしないでもないですが、まあ気にしない気にしない。
ドキュメントとかは気が向いたら。
次の目標は del.icio.us 用のラッパを作ること。それから、重複部分を一つに纏めるところ。
ヒント:仕事が沢山
以下、ソース。
#!/usr/bin/env ruby -Ku require 'time' require 'digest/sha1' require 'base64' require 'net/http' require 'rss' require 'net/http' require 'uri' USER = "USER" PASS = "PASS" class Object def self.protected_attr(*attrs) attrs.each{|att| define_method(att){ if @_updating instance_variable_get("@#{att}") else data = instance_variable_get("@#{att}").clone if Array === data data.map!{|i| i.clone} end data end } define_method("#{att}="){|other| if @_updating instance_variable_set("@#{att}", other) else raise "Not in update" end } } end end class Hatebu attr_reader :user, :pass include Digest def initialize(user, pass) @user = user @pass = pass res = get_data("/atom") body = res.body atom = RSS::Parser.parse(body, false) atom.links.each{|item| if item.rel=~ /^service\.(.+)$/ service = $1 path = URI.split(item.href)[5] instance_variable_set("@#{service}", path) end } end def delete(item) unless Entry === item item = get_entry(item) end res = send_data(:delete, item.path) if res.code == "200" true else raise "Couldn't delete entry: #{item.eid}" end end def get_entry(eid) Entry.new(self, get_data("/atom/edit/#{eid}").body) end def make_wsse nonce = '' 20.times{ nonce << rand(256) } now = Time.now.utc.iso8601 digest = Base64.encode64(SHA1.digest(nonce+now+@pass)).chomp return %Q(UsernameToken Username="#{@user}", PasswordDigest="#{digest}", Nonce="#{Base64.encode64(nonce).chomp}", Created="#{now}") end def send_data(mtd, path, *data) data << {"X-WSSE" => make_wsse} Net::HTTP.start("b.hatena.ne.jp"){|http| http.__send__(mtd, path, *data) } end def get_data(path) send_data(:get, path) end def put_data(path, data) send_data(:put, path, data) end def post_data(path, data) send_data(:post, path, data) end def edit(ident) get_data("/atom/edit/#{ident}") end def post(url, comment="") body = <<-EOS <entry xmlns="http://purl.org/atom/ns#"> <title>dummy</title> <link rel="related" type="text/html" href="http://www.example.com/" /> <summary type="text/plain">#{comment}</summary> </entry> EOS res = post_data(@post, body) if res.code == "201" Entry.new(self, res.body) else raise "Couldn't post new entry for `#{url}'" end end alias create post alias add post def entry_from_word(*word) entries_from_word(*word)[0] end def entries_from_word(*word) atom = get_data("/#@user/atomfeed?word=#{word.map{|t| URI.escape(t)}.join(';word=')}").body feed = RSS::Parser.parse(atom, false) feed.entries.map{|ent| Entry.new(self, ent)} end def entry_from_tag(*tag) entries_from_tag(*tag)[0] end def entries_from_tag(*tag) atom = get_data("/#@user/atomfeed?tag=#{tag.map{|t| URI.escape(t)}.join(';tag=')}").body feed = RSS::Parser.parse(atom, false) feed.entries.map{|ent| Entry.new(self, ent)} end def entry_from_url(url) entries_from_url(url)[0] end def entries_from_url(url) atom = get_data("/#@user/atomfeed?url=#{URI.escape url}").body feed = RSS::Parser.parse(atom, false) feed.entries.map{|ent| Entry.new(self, ent)} end def entries rss = RSS::Parser.parse(get_data("/#@user/atomfeed").body, false) rss.entries.map{|ent| Entry.new(self, ent)} end class Entry attr_reader :issued, :address, :eid, :path, :comment, :url protected_attr :title, :tags, :summary def initialize(parent, atom) @parent = parent if String === atom entry = RSS::Parser.parse(atom, false) else entry = atom end @url = entry.links.find{|i| i.rel=="related"}.href @title = entry.title.content @summary = entry.summary.content @path = URI.split(entry.links.find{|l| l.rel=="service.edit"}.href)[5] @eid = @path.split("/")[-1].to_i @tags = entry.dc_subjects.map{|i| i.content} @_updating = false end def update(&block) if block @_updating = true block.call(self) @_updating = false end @parent.put_data(@path, <<-EOS) <entry xmlns="http://purl.org/atom/ns#"> <title>#{title}</title> <summary type="text/plain">#{tags.map{|t|"["+t+"]"}.join("")}#{summary}</summary> </entry> EOS end alias edit update end end if $0 == __FILE__ hat = Hatebu.new(USER, PASS) p hat.entries ent = hat.create("http://www.example.com/", "[test] 試験") p ent.tags, ent.title, ent.url, ent.summary ent.update{|e| e.tags << "web" e.summary = "ふかをかけてごめんなさい" } p hat.entries hat.delete ent end